UI স্তরটিতে UI- সম্পর্কিত অবস্থা এবং UI লজিক থাকে, ডেটা স্তরটিতে অ্যাপ্লিকেশন ডেটা এবং ব্যবসায়িক যুক্তি থাকে৷ ব্যবসায়িক যুক্তিই আপনার অ্যাপকে মূল্য দেয়—এটি বাস্তব-বিশ্বের ব্যবসায়িক নিয়ম দ্বারা তৈরি যা নির্ধারণ করে যে কীভাবে অ্যাপ্লিকেশন ডেটা তৈরি, সংরক্ষণ এবং পরিবর্তন করা উচিত।
উদ্বেগের এই বিচ্ছেদ ডেটা স্তরটিকে একাধিক স্ক্রিনে ব্যবহার করার অনুমতি দেয়, অ্যাপের বিভিন্ন অংশের মধ্যে তথ্য ভাগ করে নিতে এবং ইউনিট পরীক্ষার জন্য UI এর বাইরে ব্যবসায়িক যুক্তি পুনরুত্পাদন করতে দেয়। ডেটা স্তরের সুবিধা সম্পর্কে আরও তথ্যের জন্য, আর্কিটেকচার ওভারভিউ পৃষ্ঠাটি দেখুন।
ডাটা লেয়ার আর্কিটেকচার
ডেটা স্তরটি সংগ্রহস্থল দিয়ে তৈরি যে প্রতিটিতে শূন্য থেকে অনেকগুলি ডেটা উত্স থাকতে পারে। আপনি আপনার অ্যাপে পরিচালনা করেন এমন প্রতিটি ভিন্ন ধরণের ডেটার জন্য আপনার একটি সংগ্রহস্থল শ্রেণী তৈরি করা উচিত। উদাহরণস্বরূপ, আপনি চলচ্চিত্র সম্পর্কিত ডেটার জন্য একটি MoviesRepository
ক্লাস বা অর্থপ্রদান সম্পর্কিত ডেটার জন্য একটি PaymentsRepository
ক্লাস তৈরি করতে পারেন।
সংগ্রহস্থল ক্লাস নিম্নলিখিত কাজের জন্য দায়ী:
- অ্যাপের বাকি অংশে ডেটা প্রকাশ করা হচ্ছে।
- তথ্য কেন্দ্রীকরণ পরিবর্তন.
- একাধিক ডেটা উত্সের মধ্যে দ্বন্দ্ব সমাধান করা।
- অ্যাপের বাকি অংশ থেকে ডেটার উৎস বিমূর্ত করা।
- ব্যবসায়িক যুক্তি ধারণ করে।
প্রতিটি ডেটা সোর্স ক্লাসের শুধুমাত্র একটি ডেটার উত্সের সাথে কাজ করার দায়িত্ব থাকা উচিত, যা একটি ফাইল, একটি নেটওয়ার্ক উত্স বা একটি স্থানীয় ডাটাবেস হতে পারে। ডেটা সোর্স ক্লাসগুলি ডেটা অপারেশনের জন্য অ্যাপ্লিকেশন এবং সিস্টেমের মধ্যে সেতু।
অনুক্রমের অন্যান্য স্তরগুলি কখনই সরাসরি ডেটা উত্স অ্যাক্সেস করতে পারে না; ডাটা লেয়ারের এন্ট্রি পয়েন্ট সবসময় রিপোজিটরি ক্লাস। স্টেট হোল্ডার ক্লাস ( ইউআই লেয়ার গাইড দেখুন) বা কেস ক্লাস ব্যবহার করুন ( ডোমেন লেয়ার গাইড দেখুন) সরাসরি নির্ভরতা হিসাবে কখনই ডেটা সোর্স থাকা উচিত নয়। এন্ট্রি পয়েন্ট হিসাবে সংগ্রহস্থল ক্লাস ব্যবহার করে আর্কিটেকচারের বিভিন্ন স্তরগুলিকে স্বাধীনভাবে স্কেল করার অনুমতি দেয়।
এই স্তর দ্বারা উন্মুক্ত করা ডেটা অপরিবর্তনীয় হওয়া উচিত যাতে এটি অন্য শ্রেণীর দ্বারা টেম্পার করা না যায়, যা এর মানগুলিকে একটি অসামঞ্জস্যপূর্ণ অবস্থায় ফেলতে পারে। অপরিবর্তনীয় ডেটা একাধিক থ্রেড দ্বারা নিরাপদে পরিচালনা করা যেতে পারে। আরও বিস্তারিত জানার জন্য থ্রেডিং বিভাগটি দেখুন।
নির্ভরতা ইনজেকশন সর্বোত্তম অনুশীলন অনুসরণ করে, সংগ্রহস্থলটি তার কন্সট্রাক্টরের নির্ভরতা হিসাবে ডেটা উত্স গ্রহণ করে:
class ExampleRepository(
private val exampleRemoteDataSource: ExampleRemoteDataSource, // network
private val exampleLocalDataSource: ExampleLocalDataSource // database
) { /* ... */ }
এপিআই প্রকাশ করুন
ডেটা স্তরের ক্লাসগুলি সাধারণত এক-শট ক্রিয়েট, রিড, আপডেট এবং ডিলিট (সিআরইউডি) কলগুলি সম্পাদন করতে বা সময়ের সাথে ডেটা পরিবর্তন সম্পর্কে অবহিত হওয়ার জন্য ফাংশন প্রকাশ করে। এই প্রতিটি ক্ষেত্রে ডেটা স্তরের নিম্নলিখিতগুলি প্রকাশ করা উচিত:
- ওয়ান-শট অপারেশন: ডাটা লেয়ারটি কোটলিনে সাসপেন্ড ফাংশন প্রকাশ করবে; এবং জাভা প্রোগ্রামিং ল্যাঙ্গুয়েজের জন্য, ডাটা লেয়ারটি এমন ফাংশনগুলিকে প্রকাশ করতে হবে যা অপারেশনের ফলাফল, অথবা RxJava
Single
,Maybe
, বাCompletable
টাইপের জন্য একটি কলব্যাক প্রদান করে। - সময়ের সাথে সাথে ডেটা পরিবর্তন সম্পর্কে অবহিত করা: ডেটা স্তরটি কোটলিনে প্রবাহ প্রকাশ করবে; এবং জাভা প্রোগ্রামিং ভাষার জন্য, ডাটা লেয়ারটি এমন একটি কলব্যাক প্রকাশ করবে যা নতুন ডেটা নির্গত করে, বা RxJava
Observable
বাFlowable
টাইপ।
class ExampleRepository(
private val exampleRemoteDataSource: ExampleRemoteDataSource, // network
private val exampleLocalDataSource: ExampleLocalDataSource // database
) {
val data: Flow<Example> = ...
suspend fun modifyData(example: Example) { ... }
}
এই নির্দেশিকায় নামকরণের নিয়মাবলী
এই নির্দেশিকায়, সংগ্রহস্থল ক্লাসের নামকরণ করা হয় সেই ডেটার নাম অনুসারে যার জন্য তারা দায়ী। কনভেনশনটি নিম্নরূপ:
তথ্যের ধরন + সংগ্রহস্থল ।
যেমন: NewsRepository
, MoviesRepository
, বা PaymentsRepository
.
ডেটা সোর্স ক্লাসের নামকরণ করা হয় তারা যে ডেটার জন্য দায়ী এবং যে সোর্স ব্যবহার করে। কনভেনশনটি নিম্নরূপ:
ডেটার ধরন + উৎসের ধরন + ডেটাসোর্স ।
ডেটার প্রকারের জন্য, আরও জেনেরিক হতে দূরবর্তী বা স্থানীয় ব্যবহার করুন কারণ বাস্তবায়ন পরিবর্তন হতে পারে। যেমন: NewsRemoteDataSource
বা NewsLocalDataSource
। উৎসটি গুরুত্বপূর্ণ হলে আরো সুনির্দিষ্ট হতে, উৎসের ধরনটি ব্যবহার করুন। যেমন: NewsNetworkDataSource
বা NewsDiskDataSource
।
একটি বাস্তবায়নের বিবরণের উপর ভিত্তি করে ডেটা উৎসের নাম দেবেন না—উদাহরণস্বরূপ, UserSharedPreferencesDataSource
—কারণ সেই ডেটা উত্স ব্যবহারকারী সংগ্রহস্থলগুলি কীভাবে ডেটা সংরক্ষণ করা হয় তা জানা উচিত নয়৷ আপনি যদি এই নিয়মটি অনুসরণ করেন, আপনি সেই উৎসটিকে কলকারী স্তরটিকে প্রভাবিত না করেই ডেটা উৎসের বাস্তবায়ন পরিবর্তন করতে পারেন (উদাহরণস্বরূপ, SharedPreferences থেকে DataStore এ স্থানান্তর করা)।
সংগ্রহস্থলের একাধিক স্তর
কিছু ক্ষেত্রে আরও জটিল ব্যবসায়ের প্রয়োজনীয়তা জড়িত, একটি সংগ্রহস্থলকে অন্যান্য সংগ্রহস্থলের উপর নির্ভর করতে হতে পারে। এটি হতে পারে কারণ জড়িত ডেটা একাধিক ডেটা উত্স থেকে একত্রিত হয়, বা দায়িত্বটি অন্য সংগ্রহস্থল ক্লাসে এনক্যাপসুলেট করা প্রয়োজন৷
উদাহরণস্বরূপ, একটি সংগ্রহস্থল যা ব্যবহারকারীর প্রমাণীকরণ ডেটা পরিচালনা করে, UserRepository
, তার প্রয়োজনীয়তাগুলি পূরণ করতে LoginRepository
এবং RegistrationRepository
এর মতো অন্যান্য সংগ্রহস্থলের উপর নির্ভর করতে পারে।
সত্যের উৎস
এটা গুরুত্বপূর্ণ যে প্রতিটি সংগ্রহস্থল সত্যের একটি একক উৎস সংজ্ঞায়িত করে। সত্যের উত্সে সর্বদা এমন ডেটা থাকে যা সামঞ্জস্যপূর্ণ, সঠিক এবং আপ-টু-ডেট। প্রকৃতপক্ষে, সংগ্রহস্থল থেকে প্রকাশিত ডেটা সর্বদা সত্যের উত্স থেকে সরাসরি আসা ডেটা হওয়া উচিত।
সত্যের উত্স একটি ডেটা উত্স হতে পারে-উদাহরণস্বরূপ, ডাটাবেস-অথবা একটি ইন-মেমরি ক্যাশে যা সংগ্রহস্থলে থাকতে পারে। সংগ্রহস্থলগুলি বিভিন্ন ডেটা উত্সকে একত্রিত করে এবং নিয়মিতভাবে সত্যের একক উত্স আপডেট করতে বা ব্যবহারকারীর ইনপুট ইভেন্টের কারণে ডেটা উত্সগুলির মধ্যে যে কোনও সম্ভাব্য দ্বন্দ্ব সমাধান করে।
আপনার অ্যাপের বিভিন্ন সংগ্রহস্থলে সত্যের বিভিন্ন উৎস থাকতে পারে। উদাহরণস্বরূপ, LoginRepository
ক্লাস সত্যের উত্স হিসাবে তার ক্যাশে ব্যবহার করতে পারে এবং PaymentsRepository
ক্লাস নেটওয়ার্ক ডেটা উত্স ব্যবহার করতে পারে।
অফলাইন-প্রথম সমর্থন প্রদানের জন্য, একটি স্থানীয় ডেটা উৎস—যেমন একটি ডাটাবেস—সত্যের প্রস্তাবিত উৎস ৷
থ্রেডিং
কল করা ডেটা উত্স এবং সংগ্রহস্থলগুলি প্রধান-নিরাপদ হওয়া উচিত — প্রধান থ্রেড থেকে কল করা নিরাপদ৷ দীর্ঘ-চলমান ব্লকিং ক্রিয়াকলাপগুলি সম্পাদন করার সময় এই ক্লাসগুলি তাদের যুক্তির সম্পাদনকে উপযুক্ত থ্রেডে নিয়ে যাওয়ার জন্য দায়ী। উদাহরণস্বরূপ, একটি ফাইল থেকে পড়ার জন্য একটি ডেটা উত্সের জন্য বা একটি বড় তালিকায় ব্যয়বহুল ফিল্টারিং সঞ্চালনের জন্য একটি সংগ্রহস্থলের জন্য এটি প্রধান-নিরাপদ হওয়া উচিত।
মনে রাখবেন যে বেশিরভাগ ডেটা উত্স ইতিমধ্যেই প্রধান-নিরাপদ API প্রদান করে যেমন রুম , রেট্রোফিট বা Ktor দ্বারা প্রদত্ত সাসপেন্ড পদ্ধতি কল। আপনার সংগ্রহস্থল এই API গুলি উপলব্ধ হলে সুবিধা নিতে পারে৷
থ্রেডিং সম্পর্কে আরও জানতে, ব্যাকগ্রাউন্ড প্রসেসিংয়ের নির্দেশিকা দেখুন। Kotlin ব্যবহারকারীদের জন্য, coroutines হল প্রস্তাবিত বিকল্প। জাভা প্রোগ্রামিং ভাষার জন্য প্রস্তাবিত বিকল্পগুলির জন্য ব্যাকগ্রাউন্ড থ্রেডে অ্যান্ড্রয়েড কাজ চালানো দেখুন।
জীবনচক্র
ডেটা লেয়ারের ক্লাসের দৃষ্টান্ত মেমরিতে থাকে যতক্ষণ না সেগুলি আবর্জনা সংগ্রহের রুট থেকে পৌঁছানো যায়—সাধারণত আপনার অ্যাপের অন্যান্য অবজেক্ট থেকে রেফারেন্স করা হয়।
যদি কোনো ক্লাসে ইন-মেমরি ডেটা থাকে—উদাহরণস্বরূপ, একটি ক্যাশে—আপনি একটি নির্দিষ্ট সময়ের জন্য সেই ক্লাসের একই উদাহরণ পুনরায় ব্যবহার করতে চাইতে পারেন। এটিকে ক্লাস ইনস্ট্যান্সের জীবনচক্র হিসাবেও উল্লেখ করা হয়।
যদি পুরো অ্যাপ্লিকেশনটির জন্য ক্লাসের দায়িত্ব গুরুত্বপূর্ণ হয়, আপনি সেই ক্লাসের একটি উদাহরণ Application
ক্লাসে দিতে পারেন। এটি এমন করে তোলে যাতে উদাহরণটি অ্যাপ্লিকেশনের জীবনচক্র অনুসরণ করে। বিকল্পভাবে, যদি আপনি শুধুমাত্র আপনার অ্যাপে একটি নির্দিষ্ট প্রবাহে একই দৃষ্টান্ত পুনঃব্যবহার করতে চান—উদাহরণস্বরূপ, নিবন্ধন বা লগইন প্রবাহ—তাহলে সেই প্রবাহের জীবনচক্রের মালিক ক্লাসের কাছে আপনার দৃষ্টান্তটি স্কোপ করা উচিত। উদাহরণস্বরূপ, আপনি একটি RegistrationRepository
স্কোপ করতে পারেন যাতে RegistrationActivity
বা রেজিস্ট্রেশন প্রবাহের নেভিগেশন গ্রাফের ইন-মেমরি ডেটা থাকে।
প্রতিটি দৃষ্টান্তের জীবনচক্র আপনার অ্যাপের মধ্যে নির্ভরতা প্রদান করার সিদ্ধান্ত নেওয়ার একটি গুরুত্বপূর্ণ কারণ। এটি সুপারিশ করা হয় যে আপনি নির্ভরতা ইনজেকশনের সর্বোত্তম অনুশীলনগুলি অনুসরণ করুন যেখানে নির্ভরতাগুলি পরিচালনা করা হয় এবং নির্ভরতা পাত্রে স্কোপ করা যেতে পারে। অ্যান্ড্রয়েডে স্কোপিং সম্পর্কে আরও জানতে, অ্যান্ড্রয়েডে স্কোপিং এবং হিল্ট ব্লগ পোস্ট দেখুন।
ব্যবসায়িক মডেলের প্রতিনিধিত্ব করুন
আপনি ডেটা স্তর থেকে যে ডেটা মডেলগুলি প্রকাশ করতে চান তা বিভিন্ন ডেটা উত্স থেকে পাওয়া তথ্যের একটি উপসেট হতে পারে৷ আদর্শভাবে, বিভিন্ন ডেটা উৎস — নেটওয়ার্ক এবং স্থানীয় উভয়ই — শুধুমাত্র আপনার আবেদনের প্রয়োজনীয় তথ্য প্রদান করবে; কিন্তু যে প্রায়ই ক্ষেত্রে না.
উদাহরণ স্বরূপ, একটি News API সার্ভার কল্পনা করুন যেটি শুধুমাত্র নিবন্ধের তথ্যই দেয় না, ইতিহাস, ব্যবহারকারীর মন্তব্য এবং কিছু মেটাডেটাও সম্পাদনা করে:
data class ArticleApiModel(
val id: Long,
val title: String,
val content: String,
val publicationDate: Date,
val modifications: Array<ArticleApiModel>,
val comments: Array<CommentApiModel>,
val lastModificationDate: Date,
val authorId: Long,
val authorName: String,
val authorDateOfBirth: Date,
val readTimeMin: Int
)
অ্যাপটির নিবন্ধ সম্পর্কে এত বেশি তথ্যের প্রয়োজন নেই কারণ এটি শুধুমাত্র তার লেখক সম্পর্কে প্রাথমিক তথ্য সহ স্ক্রিনে নিবন্ধের বিষয়বস্তু প্রদর্শন করে। মডেল ক্লাসগুলিকে আলাদা করা এবং আপনার সংগ্রহস্থলগুলিকে কেবলমাত্র সেই ডেটা প্রকাশ করতে দেওয়া একটি ভাল অভ্যাস যা অনুক্রমের অন্যান্য স্তরগুলির প্রয়োজন৷ উদাহরণস্বরূপ, ডোমেন এবং UI স্তরগুলিতে একটি Article
মডেল শ্রেণী প্রকাশ করার জন্য আপনি কীভাবে নেটওয়ার্ক থেকে ArticleApiModel
ট্রিম করতে পারেন তা এখানে রয়েছে:
data class Article(
val id: Long,
val title: String,
val content: String,
val publicationDate: Date,
val authorName: String,
val readTimeMin: Int
)
মডেল ক্লাস আলাদা করা নিম্নলিখিত উপায়ে উপকারী:
- এটি শুধুমাত্র প্রয়োজনীয় ডেটা হ্রাস করে অ্যাপ মেমরি সংরক্ষণ করে।
- এটি আপনার অ্যাপের দ্বারা ব্যবহৃত ডেটা প্রকারের সাথে বাহ্যিক ডেটা প্রকারগুলিকে অভিযোজিত করে—উদাহরণস্বরূপ, তারিখগুলি উপস্থাপন করতে আপনার অ্যাপ একটি ভিন্ন ডেটা টাইপ ব্যবহার করতে পারে৷
- এটি উদ্বেগগুলির আরও ভাল বিচ্ছেদ প্রদান করে-উদাহরণস্বরূপ, মডেল ক্লাসটি আগে থেকে সংজ্ঞায়িত করা থাকলে একটি বড় দলের সদস্যরা নেটওয়ার্ক এবং একটি বৈশিষ্ট্যের UI স্তরগুলিতে পৃথকভাবে কাজ করতে পারে৷
আপনি এই অনুশীলনটি প্রসারিত করতে পারেন এবং আপনার অ্যাপ আর্কিটেকচারের অন্যান্য অংশগুলিতেও পৃথক মডেল ক্লাস সংজ্ঞায়িত করতে পারেন — উদাহরণস্বরূপ, ডেটা উত্স ক্লাস এবং ভিউমডেলগুলিতে৷ যাইহোক, এর জন্য আপনাকে অতিরিক্ত ক্লাস এবং যুক্তি সংজ্ঞায়িত করতে হবে যা আপনার সঠিকভাবে নথিপত্র এবং পরীক্ষা করা উচিত। ন্যূনতম, এটি সুপারিশ করা হয় যে আপনি যে কোনও ক্ষেত্রে নতুন মডেল তৈরি করুন যেখানে কোনও ডেটা উত্স এমন ডেটা গ্রহণ করে যা আপনার বাকি অ্যাপের প্রত্যাশার সাথে মেলে না।
ডেটা অপারেশনের ধরন
ডেটা স্তরটি এমন ধরণের অপারেশনগুলির সাথে মোকাবিলা করতে পারে যা সেগুলি কতটা গুরুত্বপূর্ণ তার উপর ভিত্তি করে পরিবর্তিত হয়: UI-ভিত্তিক, অ্যাপ-ভিত্তিক এবং ব্যবসা-ভিত্তিক ক্রিয়াকলাপ৷
UI-ভিত্তিক অপারেশন
UI-ভিত্তিক ক্রিয়াকলাপগুলি শুধুমাত্র তখনই প্রাসঙ্গিক হয় যখন ব্যবহারকারী একটি নির্দিষ্ট স্ক্রিনে থাকে এবং ব্যবহারকারী সেই স্ক্রীন থেকে দূরে সরে গেলে সেগুলি বাতিল হয়ে যায়৷ একটি উদাহরণ ডাটাবেস থেকে প্রাপ্ত কিছু তথ্য প্রদর্শন করছে।
UI-ভিত্তিক ক্রিয়াকলাপগুলি সাধারণত UI স্তর দ্বারা ট্রিগার হয় এবং কলারের জীবনচক্র অনুসরণ করে—উদাহরণস্বরূপ, ভিউ মডেলের জীবনচক্র। একটি UI-ভিত্তিক অপারেশনের উদাহরণের জন্য একটি নেটওয়ার্ক অনুরোধ করুন বিভাগটি দেখুন।
অ্যাপ-ভিত্তিক অপারেশন
যতক্ষণ অ্যাপ খোলা থাকে ততক্ষণ অ্যাপ-ভিত্তিক ক্রিয়াকলাপগুলি প্রাসঙ্গিক। অ্যাপটি বন্ধ হয়ে গেলে বা প্রক্রিয়াটি বন্ধ হয়ে গেলে, এই অপারেশনগুলি বাতিল করা হয়। একটি উদাহরণ হল একটি নেটওয়ার্ক অনুরোধের ফলাফল ক্যাশে করা যাতে প্রয়োজন হলে এটি পরে ব্যবহার করা যেতে পারে। আরও জানতে ইমপ্লিমেন্ট ইন-মেমরি ডেটা ক্যাশিং বিভাগটি দেখুন।
এই অপারেশনগুলি সাধারণত Application
ক্লাস বা ডেটা স্তরের জীবনচক্র অনুসরণ করে। একটি উদাহরণের জন্য, স্ক্রীন বিভাগের চেয়ে একটি অপারেশন লাইভ করুন দেখুন।
ব্যবসা-ভিত্তিক অপারেশন
ব্যবসা ভিত্তিক অপারেশন বাতিল করা যাবে না. তারা মৃত্যুর প্রক্রিয়া বেঁচে থাকা উচিত. একটি উদাহরণ হল একটি ফটো আপলোড শেষ করা যা ব্যবহারকারী তাদের প্রোফাইলে পোস্ট করতে চায়৷
ব্যবসা-ভিত্তিক ক্রিয়াকলাপের জন্য সুপারিশ হল WorkManager ব্যবহার করা। আরও জানার জন্য WorkManager বিভাগ ব্যবহার করে কাজের সময়সূচী দেখুন।
ত্রুটিগুলি প্রকাশ করুন
সংগ্রহস্থল এবং ডেটার উত্সগুলির সাথে মিথস্ক্রিয়া হয় সফল হতে পারে বা ব্যর্থতা ঘটলে একটি ব্যতিক্রম নিক্ষেপ করতে পারে। কোরোটিন এবং প্রবাহের জন্য, আপনাকে কোটলিনের অন্তর্নির্মিত ত্রুটি-হ্যান্ডলিং পদ্ধতি ব্যবহার করা উচিত। সাসপেন্ড ফাংশন দ্বারা ট্রিগার হতে পারে এমন ত্রুটির জন্য, উপযুক্ত হলে try/catch
ব্লক ব্যবহার করুন; এবং প্রবাহে, catch
অপারেটর ব্যবহার করুন। এই পদ্ধতির সাহায্যে, ডেটা স্তর কল করার সময় UI স্তর ব্যতিক্রমগুলি পরিচালনা করবে বলে আশা করা হচ্ছে।
ডেটা স্তর বিভিন্ন ধরণের ত্রুটি বুঝতে এবং পরিচালনা করতে পারে এবং কাস্টম ব্যতিক্রমগুলি ব্যবহার করে সেগুলিকে প্রকাশ করতে পারে—উদাহরণস্বরূপ, একটি UserNotAuthenticatedException
।
coroutines এর ত্রুটি সম্পর্কে আরো জানতে, coroutines ব্লগ পোস্টে ব্যতিক্রম দেখুন।
সাধারণ কাজ
নিম্নলিখিত বিভাগগুলি অ্যান্ড্রয়েড অ্যাপগুলিতে সাধারণ কিছু কাজ সম্পাদন করতে ডেটা স্তরটি কীভাবে ব্যবহার এবং আর্কিটেক্ট করতে হয় তার উদাহরণ উপস্থাপন করে। উদাহরণগুলি পূর্বে নির্দেশিকায় উল্লিখিত সাধারণ সংবাদ অ্যাপের উপর ভিত্তি করে।
একটি নেটওয়ার্ক অনুরোধ করুন
একটি নেটওয়ার্ক অনুরোধ করা একটি অ্যান্ড্রয়েড অ্যাপটি সম্পাদন করতে পারে এমন একটি সাধারণ কাজ। নিউজ অ্যাপটিকে ব্যবহারকারীকে নেটওয়ার্ক থেকে আনা সর্বশেষ সংবাদ উপস্থাপন করতে হবে। অতএব, নেটওয়ার্ক ক্রিয়াকলাপগুলি পরিচালনা করার জন্য অ্যাপটির একটি ডেটা উত্স শ্রেণির প্রয়োজন: NewsRemoteDataSource
। অ্যাপের বাকি অংশে তথ্য প্রকাশ করার জন্য, একটি নতুন সংগ্রহস্থল তৈরি করা হয়েছে যা নিউজ ডেটার অপারেশন পরিচালনা করে: NewsRepository
।
প্রয়োজনীয়তা হল যে ব্যবহারকারী যখন স্ক্রীনটি খুলবে তখন সর্বদা সর্বশেষ খবর আপডেট করা দরকার। সুতরাং, এটি একটি UI-ভিত্তিক অপারেশন ।
ডেটা উত্স তৈরি করুন
ডেটা উত্সকে এমন একটি ফাংশন প্রকাশ করতে হবে যা সর্বশেষ সংবাদ প্রদান করে: ArticleHeadline
উদাহরণগুলির একটি তালিকা৷ নেটওয়ার্ক থেকে সর্বশেষ খবর পাওয়ার জন্য ডেটা উৎসকে একটি প্রধান-নিরাপদ উপায় প্রদান করতে হবে। এর জন্য, কাজটি চালানোর জন্য এটিকে CoroutineDispatcher
বা Executor
এর উপর নির্ভরতা নিতে হবে।
একটি নেটওয়ার্ক অনুরোধ করা হল একটি ওয়ান-শট কল যা একটি নতুন fetchLatestNews()
পদ্ধতি দ্বারা পরিচালিত হয়:
class NewsRemoteDataSource(
private val newsApi: NewsApi,
private val ioDispatcher: CoroutineDispatcher
) {
/**
* Fetches the latest news from the network and returns the result.
* This executes on an IO-optimized thread pool, the function is main-safe.
*/
suspend fun fetchLatestNews(): List<ArticleHeadline> =
// Move the execution to an IO-optimized thread since the ApiService
// doesn't support coroutines and makes synchronous requests.
withContext(ioDispatcher) {
newsApi.fetchLatestNews()
}
}
// Makes news-related network synchronous requests.
interface NewsApi {
fun fetchLatestNews(): List<ArticleHeadline>
}
NewsApi
ইন্টারফেস নেটওয়ার্ক API ক্লায়েন্টের বাস্তবায়ন লুকিয়ে রাখে; ইন্টারফেসটি Retrofit বা HttpURLConnection
দ্বারা সমর্থিত কিনা তা কোন পার্থক্য করে না। ইন্টারফেসের উপর নির্ভর করা আপনার অ্যাপে API বাস্তবায়নকে অদলবদলযোগ্য করে তোলে।
সংগ্রহস্থল তৈরি করুন
যেহেতু এই কাজের জন্য রিপোজিটরি ক্লাসে কোনও অতিরিক্ত যুক্তির প্রয়োজন নেই, NewsRepository
নেটওয়ার্ক ডেটা উত্সের জন্য একটি প্রক্সি হিসাবে কাজ করে৷ বিমূর্ততার এই অতিরিক্ত স্তর যুক্ত করার সুবিধাগুলি ইন-মেমরি ক্যাশিং বিভাগে ব্যাখ্যা করা হয়েছে।
// NewsRepository is consumed from other layers of the hierarchy.
class NewsRepository(
private val newsRemoteDataSource: NewsRemoteDataSource
) {
suspend fun fetchLatestNews(): List<ArticleHeadline> =
newsRemoteDataSource.fetchLatestNews()
}
কিভাবে সরাসরি UI স্তর থেকে সংগ্রহস্থল শ্রেণী ব্যবহার করতে হয় তা শিখতে, UI স্তর নির্দেশিকা দেখুন।
ইন-মেমরি ডেটা ক্যাশিং প্রয়োগ করুন
ধরুন নিউজ অ্যাপের জন্য একটি নতুন প্রয়োজনীয়তা চালু করা হয়েছে: ব্যবহারকারী যখন স্ক্রিনটি খোলে, পূর্বে অনুরোধ করা থাকলে ক্যাশে করা খবর অবশ্যই ব্যবহারকারীর কাছে উপস্থাপন করতে হবে। অন্যথায়, অ্যাপটিকে সর্বশেষ সংবাদ পেতে একটি নেটওয়ার্ক অনুরোধ করা উচিত।
নতুন প্রয়োজনীয়তার পরিপ্রেক্ষিতে, ব্যবহারকারীর অ্যাপ খোলা থাকাকালীন অ্যাপটিকে অবশ্যই মেমরিতে সর্বশেষ সংবাদ সংরক্ষণ করতে হবে। সুতরাং, এটি একটি অ্যাপ-ভিত্তিক অপারেশন ।
ক্যাশে
ইন-মেমরি ডেটা ক্যাশিং যোগ করে ব্যবহারকারী আপনার অ্যাপে থাকাকালীন আপনি ডেটা সংরক্ষণ করতে পারেন। ক্যাশে বলতে বোঝানো হয় নির্দিষ্ট সময়ের জন্য মেমরিতে কিছু তথ্য সংরক্ষণ করা—এই ক্ষেত্রে, যতক্ষণ ব্যবহারকারী অ্যাপে থাকে। ক্যাশে বাস্তবায়ন বিভিন্ন রূপ নিতে পারে। এটি একটি সাধারণ পরিবর্তনযোগ্য ভেরিয়েবল থেকে আরও পরিশীলিত শ্রেণিতে পরিবর্তিত হতে পারে যা একাধিক থ্রেডে রিড/রাইট অপারেশন থেকে রক্ষা করে। ব্যবহারের ক্ষেত্রে নির্ভর করে, ক্যাশিং রিপোজিটরি বা ডেটা সোর্স ক্লাসে প্রয়োগ করা যেতে পারে।
নেটওয়ার্ক অনুরোধের ফলাফল ক্যাশে করুন
সরলতার জন্য, NewsRepository
সর্বশেষ সংবাদ ক্যাশে করার জন্য একটি পরিবর্তনযোগ্য পরিবর্তনশীল ব্যবহার করে। বিভিন্ন থ্রেড থেকে পড়া এবং লিখতে রক্ষা করার জন্য, একটি Mutex
ব্যবহার করা হয়। শেয়ার্ড মিউটেবল স্টেট এবং কনকারেন্সি সম্পর্কে আরও জানতে, কোটলিন ডকুমেন্টেশন দেখুন।
নিম্নোক্ত বাস্তবায়নটি ভান্ডারের একটি ভেরিয়েবলের সর্বশেষ খবরের তথ্য ক্যাশে করে যা একটি Mutex
সাহায্যে লেখা-সুরক্ষিত। নেটওয়ার্ক অনুরোধের ফলাফল সফল হলে, ডেটা latestNews
ভেরিয়েবলে বরাদ্দ করা হয়।
class NewsRepository(
private val newsRemoteDataSource: NewsRemoteDataSource
) {
// Mutex to make writes to cached values thread-safe.
private val latestNewsMutex = Mutex()
// Cache of the latest news got from the network.
private var latestNews: List<ArticleHeadline> = emptyList()
suspend fun getLatestNews(refresh: Boolean = false): List<ArticleHeadline> {
if (refresh || latestNews.isEmpty()) {
val networkResult = newsRemoteDataSource.fetchLatestNews()
// Thread-safe write to latestNews
latestNewsMutex.withLock {
this.latestNews = networkResult
}
}
return latestNewsMutex.withLock { this.latestNews }
}
}
একটি অপারেশন স্ক্রীনের চেয়ে বেশি দিন লাইভ করুন
নেটওয়ার্ক অনুরোধ চলাকালীন ব্যবহারকারী স্ক্রীন থেকে দূরে সরে গেলে, এটি বাতিল করা হবে এবং ফলাফল ক্যাশে করা হবে না। এই যুক্তিটি সম্পাদন করতে NewsRepository
এর কলারের CoroutineScope
ব্যবহার করা উচিত নয়৷ পরিবর্তে, NewsRepository
একটি CoroutineScope
ব্যবহার করা উচিত যা তার জীবনচক্রের সাথে সংযুক্ত। সর্বশেষ খবর আনা একটি অ্যাপ-ভিত্তিক অপারেশন হতে হবে।
নির্ভরতা ইনজেকশনের সর্বোত্তম অনুশীলনগুলি অনুসরণ করতে, NewsRepository
এর নিজস্ব CoroutineScope
তৈরির পরিবর্তে তার কনস্ট্রাক্টরে একটি প্যারামিটার হিসাবে একটি সুযোগ পাওয়া উচিত। যেহেতু রিপোজিটরিগুলিকে তাদের বেশিরভাগ কাজ ব্যাকগ্রাউন্ড থ্রেডে করা উচিত, তাই আপনাকে Dispatchers.Default
বা আপনার নিজস্ব থ্রেড পুলের সাথে CoroutineScope
কনফিগার করা উচিত।
class NewsRepository(
...,
// This could be CoroutineScope(SupervisorJob() + Dispatchers.Default).
private val externalScope: CoroutineScope
) { ... }
যেহেতু NewsRepository
বহিরাগত CoroutineScope
এর সাথে অ্যাপ-ভিত্তিক ক্রিয়াকলাপগুলি সম্পাদন করার জন্য প্রস্তুত, এটিকে অবশ্যই ডেটা উত্সে কল করতে হবে এবং সেই সুযোগ দ্বারা শুরু করা একটি নতুন কোরোটিনের সাথে এর ফলাফল সংরক্ষণ করতে হবে:
class NewsRepository(
private val newsRemoteDataSource: NewsRemoteDataSource,
private val externalScope: CoroutineScope
) {
/* ... */
suspend fun getLatestNews(refresh: Boolean = false): List<ArticleHeadline> {
return if (refresh) {
externalScope.async {
newsRemoteDataSource.fetchLatestNews().also { networkResult ->
// Thread-safe write to latestNews.
latestNewsMutex.withLock {
latestNews = networkResult
}
}
}.await()
} else {
return latestNewsMutex.withLock { this.latestNews }
}
}
}
async
বাহ্যিক সুযোগে coroutine শুরু করতে ব্যবহৃত হয়। নেটওয়ার্ক অনুরোধ ফিরে না আসা পর্যন্ত এবং ফলাফল ক্যাশে সংরক্ষিত না হওয়া পর্যন্ত await
স্থগিত করার জন্য নতুন coroutine-এ বলা হয়েছে। যদি সেই সময়ের মধ্যে ব্যবহারকারী এখনও স্ক্রিনে থাকে, তাহলে তারা সর্বশেষ খবর দেখতে পাবে; ব্যবহারকারী স্ক্রীন থেকে দূরে সরে গেলে, await
বাতিল হয়ে যায় কিন্তু async
ভিতরে লজিক কার্যকর হতে থাকে।
CoroutineScope
এর নিদর্শন সম্পর্কে আরও জানতে এই ব্লগ পোস্টটি দেখুন।
ডিস্ক থেকে ডেটা সংরক্ষণ এবং পুনরুদ্ধার করুন
ধরুন আপনি বুকমার্ক করা খবর এবং ব্যবহারকারীর পছন্দের মতো ডেটা সংরক্ষণ করতে চান। এই ধরনের ডেটার প্রক্রিয়া মৃত্যু থেকে বাঁচতে হবে এবং ব্যবহারকারী নেটওয়ার্কের সাথে সংযুক্ত না থাকলেও অ্যাক্সেসযোগ্য হতে হবে।
আপনি যে ডেটা নিয়ে কাজ করছেন তা যদি মৃত্যুর প্রক্রিয়ায় বেঁচে থাকার প্রয়োজন হয়, তাহলে আপনাকে নিম্নলিখিত উপায়ে ডিস্কে সংরক্ষণ করতে হবে:
- বৃহৎ ডেটাসেটগুলির জন্য যেগুলিকে জিজ্ঞাসা করা প্রয়োজন, রেফারেন্সিয়াল অখণ্ডতার প্রয়োজন বা আংশিক আপডেটের প্রয়োজন, একটি রুম ডাটাবেসে ডেটা সংরক্ষণ করুন৷ News অ্যাপের উদাহরণে, সংবাদ নিবন্ধ বা লেখক ডাটাবেসে সংরক্ষণ করা যেতে পারে।
- ছোট ডেটাসেটগুলির জন্য যেগুলি শুধুমাত্র পুনরুদ্ধার করা এবং সেট করা প্রয়োজন (কোয়েরি বা আংশিক আপডেট করা নয়), ডেটাস্টোর ব্যবহার করুন। News অ্যাপের উদাহরণে, ব্যবহারকারীর পছন্দের তারিখ বিন্যাস বা অন্যান্য প্রদর্শন পছন্দগুলি ডেটাস্টোরে সংরক্ষণ করা যেতে পারে।
- JSON অবজেক্টের মতো ডেটার অংশগুলির জন্য, একটি ফাইল ব্যবহার করুন।
সত্যের উত্স বিভাগে উল্লিখিত হিসাবে, প্রতিটি ডেটা উত্স শুধুমাত্র একটি উত্সের সাথে কাজ করে এবং একটি নির্দিষ্ট ডেটা প্রকারের সাথে মিলে যায় (উদাহরণস্বরূপ, News
, Authors
, NewsAndAuthors
, বা UserPreferences
)। যে ক্লাসগুলি ডেটা উত্স ব্যবহার করে তাদের জানা উচিত নয় যে ডেটা কীভাবে সংরক্ষণ করা হয় - উদাহরণস্বরূপ, একটি ডাটাবেসে বা একটি ফাইলে৷
ডাটা সোর্স হিসেবে রুম
যেহেতু প্রতিটি ডেটা উত্সের একটি নির্দিষ্ট ধরণের ডেটার জন্য শুধুমাত্র একটি উত্সের সাথে কাজ করার দায়িত্ব থাকা উচিত, একটি রুম ডেটা উত্স হয় একটি ডেটা অ্যাক্সেস অবজেক্ট (DAO) বা ডেটাবেস নিজেই একটি প্যারামিটার হিসাবে পাবে৷ উদাহরণস্বরূপ, NewsLocalDataSource
একটি প্যারামিটার হিসাবে NewsDao
এর একটি উদাহরণ নিতে পারে এবং AuthorsLocalDataSource
AuthorsDao
এর একটি উদাহরণ নিতে পারে।
কিছু ক্ষেত্রে, যদি কোন অতিরিক্ত যুক্তির প্রয়োজন না হয়, আপনি DAO কে সরাসরি সংগ্রহস্থলে ইনজেক্ট করতে পারেন, কারণ DAO হল একটি ইন্টারফেস যা আপনি সহজেই পরীক্ষায় প্রতিস্থাপন করতে পারেন।
রুম API-এর সাথে কাজ করার বিষয়ে আরও জানতে, রুম গাইড দেখুন।
ডেটার উৎস হিসেবে ডেটাস্টোর
ডেটাস্টোর ব্যবহারকারী সেটিংসের মতো কী-মান জোড়া সংরক্ষণের জন্য উপযুক্ত। উদাহরণগুলির মধ্যে সময় বিন্যাস, বিজ্ঞপ্তির পছন্দগুলি এবং ব্যবহারকারীর পড়ার পরে সংবাদ আইটেমগুলি দেখাতে বা লুকিয়ে রাখতে পারে। ডেটাস্টোর প্রোটোকল বাফারগুলির সাথে টাইপ করা বস্তুগুলিও সংরক্ষণ করতে পারে।
অন্য যেকোন অবজেক্টের মতো, ডেটাস্টোর দ্বারা সমর্থিত একটি ডেটা উত্সে একটি নির্দিষ্ট ধরণের বা অ্যাপের একটি নির্দিষ্ট অংশের সাথে সম্পর্কিত ডেটা থাকা উচিত। ডেটাস্টোরের ক্ষেত্রে এটি আরও বেশি সত্য, কারণ ডেটাস্টোর রিডগুলি একটি প্রবাহ হিসাবে উন্মুক্ত হয় যা প্রতিবার একটি মান আপডেট করার সময় নির্গত হয়। এই কারণে, আপনার একই ডেটাস্টোরে সম্পর্কিত পছন্দগুলি সঞ্চয় করা উচিত।
উদাহরণস্বরূপ, আপনার কাছে একটি NotificationsDataStore
থাকতে পারে যা শুধুমাত্র বিজ্ঞপ্তি-সম্পর্কিত পছন্দগুলি পরিচালনা করে এবং একটি NewsPreferencesDataStore
যা শুধুমাত্র খবরের পর্দার সাথে সম্পর্কিত পছন্দগুলি পরিচালনা করে৷ এইভাবে, আপনি আপডেটগুলিকে আরও ভালভাবে স্কোপ করতে পারবেন, কারণ newsScreenPreferencesDataStore.data
ফ্লো শুধুমাত্র তখনই নির্গত হয় যখন সেই স্ক্রীনের সাথে সম্পর্কিত একটি পছন্দ পরিবর্তন করা হয়। এর মানে হল যে বস্তুর জীবনচক্র সংক্ষিপ্ত হতে পারে কারণ এটি কেবল ততক্ষণ পর্যন্ত বেঁচে থাকতে পারে যতক্ষণ না নিউজ স্ক্রীন প্রদর্শিত হয়।
DataStore API-এর সাথে কাজ করার বিষয়ে আরও জানতে, DataStore গাইড দেখুন।
ডেটা উৎস হিসেবে একটি ফাইল
একটি JSON অবজেক্ট বা একটি বিটম্যাপের মতো বড় বস্তুর সাথে কাজ করার সময়, আপনাকে একটি File
অবজেক্টের সাথে কাজ করতে হবে এবং সুইচিং থ্রেডগুলি পরিচালনা করতে হবে।
ফাইল স্টোরেজ নিয়ে কাজ করার বিষয়ে আরও জানতে, স্টোরেজ ওভারভিউ পৃষ্ঠাটি দেখুন।
WorkManager ব্যবহার করে কাজগুলি নির্ধারণ করুন
ধরুন নিউজ অ্যাপের জন্য আরেকটি নতুন প্রয়োজনীয়তা চালু করা হয়েছে: অ্যাপটিকে অবশ্যই ব্যবহারকারীকে নিয়মিত এবং স্বয়ংক্রিয়ভাবে সর্বশেষ খবর আনার বিকল্প দিতে হবে যতক্ষণ না ডিভাইসটি চার্জ হচ্ছে এবং একটি মিটারবিহীন নেটওয়ার্কের সাথে সংযুক্ত থাকবে। এটি একটি ব্যবসা-ভিত্তিক অপারেশন করে তোলে। এই প্রয়োজনীয়তাটি এমন করে যাতে ব্যবহারকারী অ্যাপটি খুললে ডিভাইসটিতে সংযোগ না থাকলেও ব্যবহারকারী সাম্প্রতিক খবর দেখতে পারেন।
ওয়ার্ক ম্যানেজার অ্যাসিঙ্ক্রোনাস এবং নির্ভরযোগ্য কাজের সময়সূচী করা সহজ করে তোলে এবং সীমাবদ্ধতা ব্যবস্থাপনার যত্ন নিতে পারে। এটি অবিরাম কাজের জন্য প্রস্তাবিত লাইব্রেরি। উপরে সংজ্ঞায়িত কাজটি সম্পাদন করার জন্য, একটি Worker
শ্রেণী তৈরি করা হয়েছে: RefreshLatestNewsWorker
। এই শ্রেণীটি সর্বশেষ সংবাদ পেতে এবং ডিস্কে ক্যাশে করার জন্য একটি নির্ভরতা হিসাবে NewsRepository
গ্রহণ করে।
class RefreshLatestNewsWorker(
private val newsRepository: NewsRepository,
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result = try {
newsRepository.refreshLatestNews()
Result.success()
} catch (error: Throwable) {
Result.failure()
}
}
এই ধরনের কাজের জন্য ব্যবসায়িক যুক্তি তার নিজস্ব শ্রেণীতে অন্তর্ভুক্ত করা উচিত এবং একটি পৃথক ডেটা উৎস হিসাবে বিবেচনা করা উচিত। ওয়ার্ক ম্যানেজার তখনই শুধুমাত্র সমস্ত সীমাবদ্ধতা পূরণ করা হলে একটি ব্যাকগ্রাউন্ড থ্রেডে কাজটি কার্যকর করা হয়েছে তা নিশ্চিত করার জন্য দায়ী থাকবে। এই প্যাটার্নটি মেনে চলার মাধ্যমে, আপনি প্রয়োজন অনুসারে দ্রুত বিভিন্ন পরিবেশে বাস্তবায়ন অদলবদল করতে পারেন।
এই উদাহরণে, এই সংবাদ-সম্পর্কিত কাজটি অবশ্যই NewsRepository
থেকে কল করতে হবে, যা একটি নির্ভরতা হিসাবে একটি নতুন ডেটা উত্স গ্রহণ করবে: NewsTasksDataSource
, নিম্নরূপ প্রয়োগ করা হয়েছে:
private const val REFRESH_RATE_HOURS = 4L
private const val FETCH_LATEST_NEWS_TASK = "FetchLatestNewsTask"
private const val TAG_FETCH_LATEST_NEWS = "FetchLatestNewsTaskTag"
class NewsTasksDataSource(
private val workManager: WorkManager
) {
fun fetchNewsPeriodically() {
val fetchNewsRequest = PeriodicWorkRequestBuilder<RefreshLatestNewsWorker>(
REFRESH_RATE_HOURS, TimeUnit.HOURS
).setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.TEMPORARILY_UNMETERED)
.setRequiresCharging(true)
.build()
)
.addTag(TAG_FETCH_LATEST_NEWS)
workManager.enqueueUniquePeriodicWork(
FETCH_LATEST_NEWS_TASK,
ExistingPeriodicWorkPolicy.KEEP,
fetchNewsRequest.build()
)
}
fun cancelFetchingNewsPeriodically() {
workManager.cancelAllWorkByTag(TAG_FETCH_LATEST_NEWS)
}
}
এই ধরনের ক্লাসের নামকরণ করা হয়েছে তারা যে ডেটার জন্য দায়ী—উদাহরণস্বরূপ, NewsTasksDataSource
বা PaymentsTasksDataSource
। একটি নির্দিষ্ট ধরণের ডেটা সম্পর্কিত সমস্ত কাজ একই ক্লাসে এনক্যাপসুলেট করা উচিত।
অ্যাপ স্টার্টআপে টাস্কটি ট্রিগার করার প্রয়োজন হলে, অ্যাপ স্টার্টআপ লাইব্রেরি ব্যবহার করে ওয়ার্কম্যানেজার অনুরোধটি ট্রিগার করার পরামর্শ দেওয়া হয় যা Initializer
থেকে সংগ্রহস্থলকে কল করে।
WorkManager API-এর সাথে কাজ করার বিষয়ে আরও জানতে, WorkManager গাইড দেখুন।
টেস্টিং
আপনার অ্যাপ পরীক্ষা করার সময় নির্ভরতা ইনজেকশন সেরা অনুশীলন সাহায্য করে। বাহ্যিক সংস্থানগুলির সাথে যোগাযোগ করে এমন ক্লাসগুলির জন্য ইন্টারফেসের উপর নির্ভর করাও সহায়ক। আপনি যখন একটি ইউনিট পরীক্ষা করেন, তখন আপনি পরীক্ষাকে নির্ধারক এবং নির্ভরযোগ্য করতে এর নির্ভরতার জাল সংস্করণগুলি ইনজেকশন করতে পারেন।
ইউনিট পরীক্ষা
ডেটা স্তর পরীক্ষা করার সময় সাধারণ পরীক্ষার নির্দেশিকা প্রযোজ্য। ইউনিট পরীক্ষার জন্য, প্রয়োজনের সময় বাস্তব বস্তু ব্যবহার করুন এবং বাহ্যিক উত্সগুলিতে পৌঁছানো যেমন ফাইল থেকে পড়া বা নেটওয়ার্ক থেকে পড়ার মতো কোনও নির্ভরতা জাল করুন।
ইন্টিগ্রেশন পরীক্ষা
বাহ্যিক উত্সগুলি অ্যাক্সেস করে এমন ইন্টিগ্রেশন পরীক্ষাগুলি কম নির্ধারক হতে থাকে কারণ তাদের একটি বাস্তব ডিভাইসে চালানো প্রয়োজন। ইন্টিগ্রেশন পরীক্ষাগুলিকে আরও নির্ভরযোগ্য করার জন্য আপনি একটি নিয়ন্ত্রিত পরিবেশের অধীনে সেই পরীক্ষাগুলি চালানোর পরামর্শ দেওয়া হচ্ছে।
ডাটাবেসের জন্য, রুম একটি ইন-মেমরি ডাটাবেস তৈরি করতে দেয় যা আপনি আপনার পরীক্ষায় সম্পূর্ণ নিয়ন্ত্রণ করতে পারেন। আরও জানতে, পরীক্ষা করুন এবং আপনার ডাটাবেস পৃষ্ঠাটি ডিবাগ করুন ।
নেটওয়ার্কিংয়ের জন্য, WireMock বা MockWebServer এর মতো জনপ্রিয় লাইব্রেরি রয়েছে যা আপনাকে নকল HTTP এবং HTTPS কল করতে দেয় এবং যাচাই করে যে অনুরোধগুলি প্রত্যাশা অনুযায়ী করা হয়েছিল।
নমুনা
নিম্নলিখিত Google নমুনাগুলি ডেটা স্তরের ব্যবহার প্রদর্শন করে৷ অনুশীলনে এই নির্দেশিকা দেখতে তাদের অন্বেষণ করুন:
আপনার জন্য প্রস্তাবিত
- দ্রষ্টব্য: জাভাস্ক্রিপ্ট বন্ধ থাকলে লিঙ্ক টেক্সট প্রদর্শিত হয়
- ডোমেন স্তর
- একটি অফলাইন-প্রথম অ্যাপ তৈরি করুন
- UI রাজ্য উত্পাদন