यूज़र इंटरफ़ेस (यूआई) लेयर में यूज़र इंटरफ़ेस (यूआई) से जुड़ी स्थिति और यूज़र इंटरफ़ेस (यूआई) लॉजिक होता है. हालांकि, डेटा लेयर में ऐप्लिकेशन का डेटा और बिज़नेस लॉजिक शामिल है. कारोबार को टारगेट करने के लिए, आपके ऐप्लिकेशन को महत्व देता है—यह वास्तविक दुनिया के व्यवसाय नियमों से बना है जो निर्धारित करते हैं कि ऐप्लिकेशन डेटा कैसे बनाया, संग्रहित किया जाना और बदला जाना चाहिए.
इन समस्याओं के आधार पर डेटा लेयर का इस्तेमाल, एक से ज़्यादा स्क्रीन, ऐप्लिकेशन के अलग-अलग हिस्सों के बीच जानकारी शेयर करें, और यूनिट टेस्टिंग के लिए यूज़र इंटरफ़ेस (यूआई) के बाहर मौजूद कारोबारी लॉजिक. इसके बारे में ज़्यादा जानकारी पाने के लिए, डेटा लेयर के फ़ायदे जानने के लिए, स्ट्रक्चर की खास जानकारी देखें पेज पर जाएं.
डेटा लेयर आर्किटेक्चर
डेटा लेयर डेटा स्टोर करने की जगहों से बनी होती है. हर डेटा लेयर में शून्य से लेकर कई अन्य स्टोरेज हो सकते हैं
डेटा सोर्स शामिल होते हैं. आपको हर अलग तरह के डेटा के लिए एक रिपॉज़िटरी क्लास बनानी चाहिए
का डेटा शामिल होता है. उदाहरण के लिए, एक MoviesRepository
बनाया जा सकता है
फ़िल्मों से जुड़े डेटा के लिए क्लास या डेटा के लिए PaymentsRepository
क्लास
भुगतान से संबंधित है.
![सामान्य आर्किटेक्चर में, डेटा लेयर के डेटा स्टोर करने की जगहें, डेटा उपलब्ध कराती हैं
ऐप्लिकेशन के अन्य हिस्सों में होने वाली बढ़ोतरी है और यह डेटा सोर्स पर निर्भर करता है.](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-data-overview.png?authuser=4&hl=hi)
रिपॉज़िटरी क्लास में ये काम किए जाते हैं:
- ऐप्लिकेशन के बाकी हिस्से में डेटा दिखाना.
- डेटा में एक ही जगह से बदलाव करने की सुविधा.
- एक से ज़्यादा डेटा सोर्स के बीच के विवाद हल करना.
- ऐप्लिकेशन के बाकी हिस्सों से डेटा के सोर्स को एब्सट्रैक्ट करते हैं.
- इसमें कारोबारी नियम शामिल हैं.
हर डेटा सोर्स क्लास के लिए, सिर्फ़ एक क्लास के साथ काम करना डेटा का सोर्स होता है. यह कोई फ़ाइल, नेटवर्क सोर्स या लोकल डेटाबेस हो सकता है. डेटा सोर्स क्लास, डेटा के लिए ऐप्लिकेशन और सिस्टम के बीच की दूरी होती है कार्रवाइयां.
क्रम में अन्य लेयर को कभी भी डेटा सोर्स को सीधे ऐक्सेस नहीं करना चाहिए; यह डेटा लेयर के एंट्री पॉइंट, हमेशा रिपॉज़िटरी क्लास होते हैं. स्टेट होल्डर क्लास (यूज़र इंटरफ़ेस (यूआई) लेयर से जुड़ी गाइड देखें) या इसका इस्तेमाल करें केस क्लास (डोमेन लेयर गाइड देखें) को डायरेक्ट डिपेंडेंसी के तौर पर कभी भी डेटा सोर्स नहीं होता. रिपॉज़िटरी क्लास का इस तौर पर इस्तेमाल करना एंट्री पॉइंट की मदद से, आर्किटेक्चर की अलग-अलग लेयर को स्केल किया जा सकता है स्वतंत्र रूप से काम करता है.
इस लेयर के सामने आने वाले डेटा में बदलाव नहीं किया जा सकता, ताकि यह के साथ छेड़छाड़ की जाती है, जिससे इसकी वैल्यू को असंगत स्थिति. नहीं बदले जा सकने वाले डेटा को कई थ्रेड. ज़्यादा जानकारी के लिए, थ्रेडिंग सेक्शन देखें.
डिपेंडेंसी इंजेक्शन के सबसे सही तरीके अपनाना, रिपॉज़िटरी, डेटा सोर्स को अपने कंस्ट्रक्टर में डिपेंडेंसी के तौर पर लेता है:
class ExampleRepository(
private val exampleRemoteDataSource: ExampleRemoteDataSource, // network
private val exampleLocalDataSource: ExampleLocalDataSource // database
) { /* ... */ }
डेटा को सार्वजनिक करने से जुड़े एपीआई
डेटा लेयर की क्लास आम तौर पर एक बार में एक शॉट में फ़ंक्शन बनाने के लिए, फ़ंक्शन दिखाती हैं. कॉल के लिए रीड, अपडेट, और मिटाएं (सीआरयूडी) कॉल या डेटा में हुए बदलाव की सूचना पाने के लिए समय. इनमें से हर मामले में डेटा लेयर को नीचे दी गई जानकारी दिखनी चाहिए:
- वन-शॉट ऑपरेशन: डेटा लेयर को सस्पेंड फ़ंक्शन को
Kotlin; और Java प्रोग्रामिंग भाषा के लिए, डेटा लेयर को
फ़ंक्शन जो कार्रवाई के नतीजे को सूचित करने के लिए कॉलबैक देते हैं, या
RxJava
Single
,Maybe
याCompletable
टाइप. - समय के साथ डेटा में होने वाले बदलावों की सूचना पाने के लिए: डेटा लेयर को जानकारी ज़ाहिर करनी चाहिए
Kotlin में फ़्लो; और Java प्रोग्रामिंग भाषा के लिए,
डेटा लेयर को एक कॉलबैक को दिखाना चाहिए, जो नए डेटा या 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
.
डेटा सोर्स क्लास को उस डेटा के नाम पर रखा जाता है जिसके लिए वे ज़िम्मेदार हैं और इस्तेमाल करते हैं. कन्वेंशन इस तरह है:
डेटा का टाइप + सोर्स का टाइप + DataSource.
डेटा के प्रकार के लिए, ज़्यादा सामान्य होने के लिए रिमोट या लोकल का इस्तेमाल करें क्योंकि
इस्तेमाल करने के तरीके बदल सकते हैं. उदाहरण के लिए: NewsRemoteDataSource
या
NewsLocalDataSource
. अगर सोर्स ज़रूरी है, तो ज़्यादा सटीक जानकारी देने के लिए,
सोर्स का टाइप. उदाहरण के लिए: NewsNetworkDataSource
या
NewsDiskDataSource
.
लागू करने की जानकारी के आधार पर डेटा सोर्स का नाम न लिखें—उदाहरण के लिए,
UserSharedPreferencesDataSource
—ऐसा इसलिए, क्योंकि डेटा स्टोर करने की ऐसी जगहें जो उस डेटा सोर्स का इस्तेमाल करती हैं
को पता नहीं होता कि डेटा कैसे सहेजा जाता है. इस नियम का पालन करने पर,
डेटा सोर्स को लागू करना (उदाहरण के लिए,
इसके लिए SharedPreferences
DataStore) से संपर्क करने में मदद मिलती है.
लेयर होती है जो उस सोर्स को कॉल करती है.
डेटा स्टोर करने की जगहों के कई लेवल
कारोबार की ज़्यादा जटिल शर्तों के कुछ मामलों में, डेटा स्टोर करने की जगह डेटा स्टोर करने की दूसरी जगहों पर निर्भर रहना पड़ता है. ऐसा इसलिए हो सकता है, क्योंकि इस प्रोसेस में शामिल डेटा कई डेटा सोर्स से इकट्ठा किया गया डेटा या रिस्पॉन्स की ज़रूरतों की वजह से को किसी अन्य रिपॉज़िटरी क्लास में शामिल किया जाएगा.
उदाहरण के लिए, रिपॉज़िटरी जो उपयोगकर्ता की पुष्टि करने वाला डेटा मैनेज करती है,
UserRepository
, डेटा स्टोर करने की दूसरी जगहों पर निर्भर कर सकता है, जैसे कि LoginRepository
और RegistrationRepository
की मदद से ऐसा किया जा सकता है.
![उदाहरण में, UserRepository दो अन्य रिपॉज़िटरी क्लास पर निर्भर करता है:
लॉगिन रिपॉज़िटरी, जो दूसरे लॉगिन डेटा सोर्स पर निर्भर करती है; और
रजिस्ट्रेशन डेटा स्टोर करने की जगह, जो रजिस्ट्रेशन के अन्य डेटा सोर्स पर निर्भर करती है.](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-data-multiple-repos.png?authuser=4&hl=hi)
सच्चाई का सोर्स
यह ज़रूरी है कि हर रिपॉज़िटरी (डेटा स्टोर करने की जगह) सिर्फ़ एक ही सोर्स के बारे में बताती हो. सोर्स में हमेशा ऐसा डेटा होता है जो एक जैसा, सही, और अप-टू-डेट होता है. तय सीमा में डेटा स्टोर करने की जगह से एक्सपोज़ किया गया डेटा सीधे तौर पर सोर्स से लेना.
सबसे सटीक जानकारी का सोर्स, कोई डेटा सोर्स हो सकता है. उदाहरण के लिए, कोई डेटाबेस या इन-मेमोरी कैश मेमोरी में सेव हो सकता है. डेटा स्टोर करने की जगहें मर्ज हो जाती हैं अलग-अलग डेटा सोर्स का इस्तेमाल करके, डेटा के बीच होने वाले किसी भी संभावित टकराव को हल किया जा सकता है एक ही सोर्स को नियमित तौर पर अपडेट करने के लिए या उपयोगकर्ता से मिले इनपुट के आधार पर, इवेंट.
आपके ऐप्लिकेशन में डेटा स्टोर करने की अलग-अलग जगहों पर, जानकारी देने के अलग-अलग सोर्स हो सकते हैं. इसके लिए
उदाहरण के लिए, LoginRepository
क्लास अपनी कैश मेमोरी का इस्तेमाल, सच्चाई के सोर्स के तौर पर कर सकती है
और PaymentsRepository
क्लास, नेटवर्क डेटा सोर्स का इस्तेमाल कर सकती है.
ऑफ़लाइन तरीके से सहायता पाने के लिए, एक स्थानीय डेटा सोर्स—जैसे कि डेटाबेस—यह सत्य का सुझाया गया स्रोत है.
थ्रेडिंग
कॉल करने के डेटा सोर्स और डेटा स्टोर करने की जगहों की जानकारी मुख्य तौर पर सुरक्षित होनी चाहिए. इसका मतलब है कि इन डिवाइसों से कॉल किया जा सकता है मुख्य थ्रेड. इन क्लास की ज़िम्मेदारी है कि वे अपने लंबे समय तक चलने वाली ब्लॉकिंग लागू करते समय, सही थ्रेड के लिए लॉजिक कार्रवाइयां. उदाहरण के लिए, डेटा सोर्स के लिए यह मुख्य तौर पर, सुरक्षित फ़ाइल का इस्तेमाल किया जा सकता है. इसके अलावा, रिपॉज़िटरी का इस्तेमाल करके
ध्यान दें कि ज़्यादातर डेटा सोर्स पहले से ही मुख्य तौर पर सुरक्षित एपीआई उपलब्ध कराते हैं. जैसे, निलंबन Room से मिले तरीके की जानकारी रेट्रोफ़िट या Ktor. डेटा स्टोर करने की जगह पर ये काम किए जा सकते हैं उपलब्ध होने पर इन एपीआई का फ़ायदा उठा सकते हैं.
थ्रेडिंग के बारे में ज़्यादा जानने के लिए, बैकग्राउंड के बारे में गाइड देखें प्रोसेस जारी है. Kotlin के उपयोगकर्ताओं के लिए, कोरूटीन इस्तेमाल करने का सुझाव दिया जाता है. दौड़ना देखें बैकग्राउंड थ्रेड में Android टास्क Java प्रोग्रामिंग भाषा के लिए सुझाए गए विकल्प.
लाइफ़साइकल
डेटा लेयर में क्लास के इंस्टेंस तब तक मेमोरी में बने रहते हैं, जब तक वे कूड़ा इकट्ठा करने वाले रूट से ऐक्सेस किया जा सकता है—आम तौर पर, दूसरे सोर्स से चीज़ें मौजूद हैं.
अगर किसी क्लास में मेमोरी में मौजूद डेटा है, तो शायद आप कैश मेमोरी का डेटा फिर से इस्तेमाल करना चाहें खास समयावधि के लिए क्लास के एक ही मामले को दिखाता है. यह भी है इसे क्लास इंस्टेंस की लाइफ़साइकल कहा जाता है.
अगर पूरे ऐप्लिकेशन के लिए क्लास की ज़िम्मेदारी ज़रूरी है, तो ये काम करें
स्कोप में उस क्लास का इंस्टेंस, Application
क्लास में शामिल किया गया है. इससे ऐसा होता है
इंस्टेंस, ऐप्लिकेशन के लाइफ़साइकल के बाद का है. इसके अलावा, अगर आपको सिर्फ़
आपके ऐप्लिकेशन में किसी खास फ़्लो में, उसी इंस्टेंस का फिर से इस्तेमाल करना हो—उदाहरण के लिए,
रजिस्ट्रेशन या लॉगिन फ़्लो को ट्रैक करना चाहिए—तो आपको इंस्टेंस को क्लास से जोड़ना चाहिए
जो उस फ़्लो के लाइफ़साइकल का मालिक है. उदाहरण के लिए, आप
RegistrationRepository
जिसमें मेमोरी में मौजूद डेटा होता है
RegistrationActivity
या नेविगेशन
इसका ग्राफ़
रजिस्ट्रेशन फ़्लो.
हर इंस्टेंस का लाइफ़साइकल अहम होता है, ताकि यह तय किया जा सके कि यूआरएल कैसे उपलब्ध कराना है पर निर्भर है. हमारा सुझाव है कि आप डिपेंडेंसी का पालन करें इंजेक्शन के सबसे सही तरीके, जहां डिपेंडेंसी मैनेज किए जाते हैं और इनके दायरे में डिपेंडेंसी कंटेनर हो सकते हैं. इस बारे में ज़्यादा जानने के लिए Android में दायरा बढ़ाएं, Android में दायरा बढ़ाएं और हिलना ब्लॉग पोस्ट.
कारोबार के मॉडल दिखाना
आपको डेटा लेयर से जिस डेटा मॉडल को दिखाना है वह इसका सबसेट हो सकता है अलग-अलग डेटा सोर्स से मिलने वाली जानकारी का इस्तेमाल करती है. आम तौर पर, अलग-अलग डेटा सोर्स—नेटवर्क और लोकल, दोनों तरह के डेटा सोर्स को सिर्फ़ वही जानकारी देनी चाहिए आपके ऐप्लिकेशन की ज़रूरतें; लेकिन अक्सर ऐसा नहीं होता है.
उदाहरण के लिए, मान लें कि 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
)
ऐप्लिकेशन को लेख के बारे में ज़्यादा जानकारी की ज़रूरत नहीं है, क्योंकि यह सिर्फ़
स्क्रीन पर लेख के कॉन्टेंट के साथ-साथ, बुनियादी जानकारी दिखाता है
पोस्ट करता है. मॉडल क्लास को अलग-अलग करना एक अच्छा तरीका है. इसके अलावा,
डेटा स्टोर करने की जगहें सिर्फ़ वह डेटा दिखाती हैं जिसे हैरारकी की दूसरी लेयर में मौजूद हैं
सभी ज़रूरी शर्तें पूरी करता है. उदाहरण के लिए, यहां बताया गया है कि ArticleApiModel
में से किस तरीके से काट-छांट की जा सकती है
डोमेन और यूज़र इंटरफ़ेस (यूआई) में Article
मॉडल क्लास दिखाने के लिए नेटवर्क
लेयर:
data class Article(
val id: Long,
val title: String,
val content: String,
val publicationDate: Date,
val authorName: String,
val readTimeMin: Int
)
मॉडल क्लास को अलग करना इन तरीकों से फ़ायदेमंद है:
- यह ऐप्लिकेशन की मेमोरी में बचत करने के लिए, ज़रूरत के मुताबिक डेटा उपलब्ध कराता है.
- यह बाहरी डेटा टाइप को, आपके ऐप्लिकेशन में इस्तेमाल किए जाने वाले डेटा टाइप के हिसाब से बदलता है—उदाहरण के लिए, आपका ऐप्लिकेशन में तारीखों को दिखाने के लिए, किसी अलग तरह का डेटा इस्तेमाल किया जा सकता है.
- इससे समस्याओं को अलग-अलग तरीके से समझने में मदद मिलती है. उदाहरण के लिए, एक बड़ी टीम के सदस्य किसी सुविधा के नेटवर्क और यूज़र इंटरफ़ेस (यूआई) लेयर पर अलग-अलग काम कर सकता है, अगर मॉडल क्लास पहले से तय कर दिया जाता है.
आप इस प्रैक्टिस को बढ़ा सकते हैं और इसके दूसरे हिस्सों में अलग-अलग मॉडल क्लास परिभाषित कर सकते हैं आपके ऐप्लिकेशन के आर्किटेक्चर को भी बनाया जा सकता है. उदाहरण के लिए, डेटा सोर्स क्लास और ViewModels. हालांकि, इसके लिए आपको ऐसी अतिरिक्त क्लास और लॉजिक तय करना होगा जो आपको अच्छी तरह से दस्तावेज़ और परीक्षण करना चाहिए. कम से कम आपको यह सुझाव दिया जाता है कि तो ऐसे किसी भी मामले में नए मॉडल बनाएं जहां डेटा सोर्स को ऐसा डेटा मिलता है जो ऐप्लिकेशन की ज़रूरी शर्तों से मैच करें.
डेटा से जुड़ी कार्रवाइयां
डेटा लेयर, अलग-अलग तरह की कार्रवाइयों से निपट सकता है. यह इस बात पर निर्भर करता है कि ज़रूरत के हिसाब से किस तरह की कार्रवाइयां की जा सकती हैं वे हैं: यूज़र इंटरफ़ेस (यूआई) पर आधारित, ऐप्लिकेशन पर आधारित, और कारोबार से जुड़े ऑपरेशन.
यूज़र इंटरफ़ेस (यूआई) पर आधारित कार्रवाइयां
यूज़र इंटरफ़ेस (यूआई) की मदद से की जाने वाली कार्रवाइयां सिर्फ़ तब काम की होती हैं, जब उपयोगकर्ता किसी खास स्क्रीन पर होता है, और जब उपयोगकर्ता उस स्क्रीन से दूर जाता है, तब वे अनुरोध रद्द हो जाते हैं. इसका एक उदाहरण है डेटाबेस से मिले कुछ डेटा को दिखाता है.
यूज़र इंटरफ़ेस (यूआई) से चलने वाली कार्रवाइयां, आम तौर पर यूज़र इंटरफ़ेस (यूआई) लेयर से ट्रिगर होती हैं. इसके बाद, ये कार्रवाइयां कॉलर का लाइफ़साइकल—उदाहरण के लिए, ViewModel का लाइफ़साइकल. देखें यूज़र इंटरफ़ेस (यूआई) पर आधारित उदाहरण के लिए नेटवर्क का अनुरोध सेक्शन कार्रवाई.
ऐप्लिकेशन के मुताबिक काम करना
जब तक ऐप्लिकेशन खुला रहता है, तब तक ऐप्लिकेशन पर की जाने वाली कार्रवाइयां प्रासंगिक रहती हैं. अगर ऐप्लिकेशन या प्रक्रिया समाप्त हो जाती है, तो ये ऑपरेशन रद्द हो जाते हैं. इसका एक उदाहरण है नेटवर्क अनुरोध के नतीजे को कैश मेमोरी में सेव करना, ताकि ज़रूरत पड़ने पर बाद में इसका इस्तेमाल किया जा सके. जानने के लिए, मेमोरी में डेटा को कैश मेमोरी में सेव करने की सुविधा लागू करना सेक्शन देखें वगैरह को कॉपी करने का विकल्प है.
आम तौर पर, ये कार्रवाइयां Application
क्लास की लाइफ़साइकल या
को ट्रैक किया जा सकता है. उदाहरण के लिए, किसी कार्रवाई को
स्क्रीन सेक्शन में अपने-आप जुड़ जाते हैं.
बिज़नेस-ओरिएंटेड ऑपरेशंस
कारोबार के लिए की गई कार्रवाइयों को रद्द नहीं किया जा सकता. उन्हें इस प्रक्रिया से बचना चाहिए मौत. उदाहरण के लिए, ऐसी फ़ोटो को अपलोड करना पूरा करना जिसे उपयोगकर्ता पोस्ट करना चाहता है प्रोफ़ाइल में पोस्ट करने की सुविधा मिलती है.
कारोबार के हिसाब से काम करने के लिए, WorkManager का इस्तेमाल करने का सुझाव दिया जाता है. यहां जाएं: ज़्यादा जानने के लिए, WorkManager का इस्तेमाल करके टास्क शेड्यूल करें सेक्शन.
गड़बड़ियां दिखाएं
डेटा स्टोर करने की जगहों और डेटा के सोर्स के साथ इंटरैक्शन हो सकता है या उसे रोका जा सकता है
कोई गड़बड़ी होने पर अपवाद. कोरूटीन और फ़्लो के लिए, आपको
Kotlin में पहले से मौजूद गड़बड़ियों को मैनेज करना
किस तरह काम करते हैं. इसके लिए
निलंबित फ़ंक्शन से ट्रिगर होने वाली गड़बड़ियां. try/catch
ब्लॉक का इस्तेमाल तब करें, जब
उचित हो; और फ़्लो में,
catch
ऑपरेटर का इस्तेमाल करें. इस तरीके से, यूआई लेयर से अपवादों को हैंडल करने की उम्मीद की जाती है. ऐसा तब होता है, जब
डेटा लेयर को कॉल करने की सुविधा मिलती है.
डेटा लेयर अलग-अलग तरह की गड़बड़ियों को समझ सकता है और उन्हें हैंडल कर सकता है. साथ ही, उन्हें
कस्टम अपवादों का इस्तेमाल करके उन्हें अपडेट करें—उदाहरण के लिए, UserNotAuthenticatedException
.
कोरूटीन में होने वाली गड़बड़ियों के बारे में ज़्यादा जानने के लिए, कोरूटीन ब्लॉग पोस्ट.
सामान्य काम
नीचे दिए गए सेक्शन में डेटा को इस्तेमाल करने और आर्किटेक्ट करने के तरीके के उदाहरण दिए गए हैं लेयर का इस्तेमाल करें. इसके उदाहरण यहां दिए गए हैं यह सुझाव, गाइड में बताए गए सामान्य समाचार ऐप्लिकेशन के हिसाब से जनरेट किया गया है.
नेटवर्क का अनुरोध करें
नेटवर्क के लिए अनुरोध करना, किसी Android ऐप्लिकेशन के ज़रिए किए जाने वाले सबसे सामान्य कामों में से एक है
करना. समाचार ऐप्लिकेशन के लिए यह ज़रूरी है कि वह उपयोगकर्ता को ताज़ा खबरें दिखाए
नेटवर्क से फ़ेच किया गया. इसलिए, ऐप्लिकेशन को मैनेज करने के लिए डेटा सोर्स क्लास की ज़रूरत होती है
नेटवर्क ऑपरेशन: NewsRemoteDataSource
. यह जानकारी
बाकी ऐप्लिकेशन, एक नया रिपॉज़िटरी (डेटा स्टोर करने की जगह) है, जो समाचार से जुड़े डेटा पर काम करता है
बनाया गया: NewsRepository
.
आवश्यक बात यह है कि जब उपयोगकर्ता स्क्रीन खोलता है. इसलिए, यह यूज़र इंटरफ़ेस (यूआई) के हिसाब से किया गया ऑपरेशन है.
डेटा सोर्स बनाना
डेटा सोर्स को एक ऐसा फ़ंक्शन दिखाना होगा जो ताज़ा खबरें दिखाता हो: सूची
कुल 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
इंटरफ़ेस, नेटवर्क एपीआई क्लाइंट को लागू करने की प्रोसेस को छिपाता है; यह
इससे कोई अंतर नहीं पड़ता कि इंटरफ़ेस का समर्थन
Retrofit या
HttpURLConnection
. भरोसा रखें
इंटरफ़ेस में, एपीआई लागू करने की सुविधा को आपके ऐप्लिकेशन में बदला जा सकता है.
डेटा स्टोर करने की जगह बनाना
इस टास्क के लिए रिपॉज़िटरी क्लास में किसी दूसरे लॉजिक की ज़रूरत नहीं होती.
NewsRepository
, नेटवर्क डेटा सोर्स के लिए प्रॉक्सी के तौर पर काम करता है. इसके फ़ायदे
ऐब्स्ट्रक्शन की इस अतिरिक्त लेयर को जोड़ने के बारे में, इन-मेमोरी
कैश मेमोरी में सेव करने की प्रोसेस सेक्शन में.
// NewsRepository is consumed from other layers of the hierarchy.
class NewsRepository(
private val newsRemoteDataSource: NewsRemoteDataSource
) {
suspend fun fetchLatestNews(): List<ArticleHeadline> =
newsRemoteDataSource.fetchLatestNews()
}
यूज़र इंटरफ़ेस (यूआई) लेयर से, रिपॉज़िटरी क्लास का इस्तेमाल करने का तरीका जानने के लिए, यूज़र इंटरफ़ेस (यूआई) लेयर गाइड.
मेमोरी में मौजूद डेटा को कैश मेमोरी में सेव करने की सुविधा लागू करें
मान लीजिए कि समाचार ऐप्लिकेशन ने एक नई शर्त जोड़ी है. जैसे: जब उपयोगकर्ता Google News ऐप्लिकेशन खोलता है तो उपयोगकर्ता को अनुरोध किए जाने पर कैश मेमोरी में सेव किए गए समाचार दिखाए जाने चाहिए पहले इस्तेमाल कर रहे थे. अगर ऐसा नहीं है, तो ऐप्लिकेशन को नए अपडेट को फ़ेच करने के लिए नेटवर्क का अनुरोध करना चाहिए समाचार.
नई शर्त को ध्यान में रखते हुए, यह ज़रूरी है कि ऐप्लिकेशन में ताज़ा खबरें, डिवाइस की मेमोरी में सेव रहें उपयोगकर्ता ने ऐप्लिकेशन खोला हो. इसलिए, यह ऐप्लिकेशन के मुताबिक बनाई गई कार्रवाई है.
कैश मेमोरी
उपयोगकर्ता के ऐप्लिकेशन का इस्तेमाल करते समय, मेमोरी में मौजूद डेटा को जोड़कर डेटा को सुरक्षित रखा जा सकता है कैश मेमोरी. कैश मेमोरी का मकसद किसी खास काम के लिए मेमोरी में कुछ जानकारी सेव करना होता है समय की जानकारी—इस मामले में, जब तक कि उपयोगकर्ता ऐप्लिकेशन में है. कैशे लागू करने के कई तरीके हो सकते हैं. यह किसी सामान्य म्यूटेबल (म्यूटेबल) से अलग हो सकता है वैरिएबल को एक बेहद जटिल क्लास में बदल देता है, जो रीड/राइट ऑपरेशन से बचाता है को ट्रैक किया जा सकता है. इस्तेमाल के उदाहरण के आधार पर, कैश मेमोरी में सेव करने की सुविधा डेटा स्टोर करने की जगह पर या डेटा सोर्स क्लास में.
नेटवर्क अनुरोध के नतीजे को कैश मेमोरी में सेव करें
इसे आसानी से समझने के लिए, 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
. क्योंकि डेटा संग्रह स्थान को अपना ज़्यादातर काम
बैकग्राउंड थ्रेड है, तो आपको CoroutineScope
को इनमें से किसी एक के साथ कॉन्फ़िगर करना चाहिए
Dispatchers.Default
या अपने थ्रेड पूल का इस्तेमाल करें.
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
का इस्तेमाल किया जाता है. await
को कॉल किया जाता है
नए कोरूटीन पर तब तक के लिए रोक लगा दी जाती है, जब तक नेटवर्क अनुरोध वापस नहीं आ जाता और
नतीजे को कैश मेमोरी में सेव किया जाता है. अगर उस समय तक उपयोगकर्ता स्क्रीन पर ही रहता है,
तब उन्हें ताज़ा खबरें दिखेंगी; अगर उपयोगकर्ता स्क्रीन से दूर जाए,
await
रद्द कर दिया गया है. हालांकि, async
में मौजूद लॉजिक काम करता है.
यह ब्लॉग देखें
पोस्ट
CoroutineScope
पैटर्न के बारे में ज़्यादा जानें.
डिस्क से डेटा सेव करें और वापस पाएं
मान लीजिए कि आप बुकमार्क की गई खबरें और उपयोगकर्ता की प्राथमिकताएं जैसा डेटा सेव करना चाहते हैं. इस तरह का डेटा इसलिए ज़रूरी है, ताकि वह मौत की वजह से पूरी तरह से सुरक्षित रहे और उस डेटा को तब भी ऐक्सेस किया जा सके, जब उपयोगकर्ता नेटवर्क से कनेक्ट नहीं है.
जिस डेटा के साथ काम किया जा रहा है, अगर वह मौत को भी बचा सकता है, तो आपको इसे डिस्क पर नीचे दिए किसी एक तरीके से सेव करें:
- अगर बड़े डेटासेट के लिए क्वेरी की जानी चाहिए, तो रेफ़रेंस इंटिग्रिटी की ज़रूरत होती है या कुछ अपडेट की ज़रूरत है, तो डेटा को रूम डेटाबेस में सेव करें. News ऐप्लिकेशन में उदाहरण के लिए, समाचार रिपोर्ट या लेखकों को डेटाबेस में सेव किया जा सकता है.
- उन छोटे डेटासेट के लिए जिन्हें सिर्फ़ फिर से हासिल करके सेट करना होता है (न कि क्वेरी या आंशिक रूप से अपडेट किया गया है), तो DataStore का इस्तेमाल करें. समाचार ऐप्लिकेशन के उदाहरण में, उपयोगकर्ता का आपकी पसंद की तारीख के फ़ॉर्मैट या डिसप्ले की अन्य प्राथमिकताओं को DataStore.
- JSON ऑब्जेक्ट जैसे डेटा के कई हिस्सों के लिए, file का इस्तेमाल करें.
जैसा कि सही जानकारी का सोर्स सेक्शन में बताया गया है, हर डेटा
सोर्स सिर्फ़ एक सोर्स के साथ काम करता है और एक खास डेटा टाइप से जुड़ा होता है (इसके लिए
उदाहरण के लिए, News
, Authors
, NewsAndAuthors
या UserPreferences
). क्लास
जो डेटा सोर्स का इस्तेमाल करते हैं उन्हें यह नहीं पता होना चाहिए कि डेटा कैसे सेव किया जाता है—उदाहरण के लिए,
डेटाबेस या फ़ाइल में.
डेटा सोर्स के तौर पर रूम
दरअसल, हर डेटा सोर्स को सिर्फ़ एक डेटा सोर्स के साथ काम करने की ज़िम्मेदारी लेनी चाहिए
एक खास टाइप के डेटा का सोर्स है, तो रूम के डेटा सोर्स को
डेटा ऐक्सेस ऑब्जेक्ट (डीएओ) या
एक पैरामीटर के तौर पर रिकॉर्ड होता है. उदाहरण के लिए, NewsLocalDataSource
NewsDao
को पैरामीटर के रूप में दिखाया है और AuthorsLocalDataSource
AuthorsDao
का इंस्टेंस.
कुछ मामलों में, अगर किसी अलग तरीके की ज़रूरत नहीं पड़ती, तो सीधे डीएओ को इंजेक्ट किया जा सकता है को रिपॉज़िटरी में एक्सपोर्ट किया जा सकता है, क्योंकि डीएओ एक ऐसा इंटरफ़ेस है जिसे आसानी से बदला जा सकता है का इस्तेमाल किया है.
Room API के साथ काम करने के बारे में ज़्यादा जानने के लिए, रूम देखें गाइड देखें.
डेटा सोर्स के तौर पर DataStore
स्टोर करने के लिए, DataStore सबसे सही तरीका है की-वैल्यू पेयर, जैसे कि उपयोगकर्ता सेटिंग. उदाहरण के लिए, समय का फ़ॉर्मैट, सूचना प्राथमिकता और उपयोगकर्ता के बाद समाचार आइटम दिखाने या छिपाने की अनुमति है ने उन्हें पढ़ लिया है. DataStore टाइप किए गए ऑब्जेक्ट को प्रोटोकॉल के साथ भी स्टोर कर सकता है बफ़र.
किसी अन्य ऑब्जेक्ट की तरह, DataStore के साथ काम करने वाले डेटा सोर्स में किसी खास तरह का या ऐप्लिकेशन के किसी खास हिस्से का डेटा. यह है और भी सही, क्योंकि DataStore से रीड को एक फ़्लो के रूप में दिखाया जाता है जो वैल्यू को हर बार अपडेट करने पर उत्सर्जन होता है. इस वजह से, आपको अपनी संबंधित प्राथमिकताएं एक ही DataStore में डालें.
उदाहरण के लिए, आपके पास एक ऐसा NotificationsDataStore
हो सकता है जो सिर्फ़
सूचना से जुड़ी प्राथमिकताएं और सिर्फ़ NewsPreferencesDataStore
यह हैंडल, न्यूज़ स्क्रीन से जुड़ी प्राथमिकताओं को हैंडल करता है. इस तरह, आपके पास कॉन्टेंट के दायरे
अपडेट बेहतर होते हैं, क्योंकि सिर्फ़ newsScreenPreferencesDataStore.data
फ़्लो में
यह तब ट्रिगर होता है, जब उस स्क्रीन से जुड़ी कोई प्राथमिकता बदली जाती है. इसका मतलब यह भी है कि
ऑब्जेक्ट का लाइफ़साइकल कम हो सकता है, क्योंकि यह तब तक ही चल सकता है, जब तक
तो खबरों वाली स्क्रीन दिखेगी.
DataStore API के साथ काम करने के बारे में ज़्यादा जानने के लिए, DataStore जाएं गाइड देखें.
डेटा सोर्स के तौर पर फ़ाइल
JSON ऑब्जेक्ट या बिटमैप जैसे बड़े ऑब्जेक्ट के साथ काम करते समय, आपको ये काम करने होंगे
File
ऑब्जेक्ट के साथ काम करते हैं और थ्रेड स्विच करने को हैंडल करते हैं.
फ़ाइल के स्टोरेज के साथ काम करने के बारे में ज़्यादा जानने के लिए, स्टोरेज के साथ काम करने के बारे में जानकारी देखें खास जानकारी पेज पर जाएं.
WorkManager का इस्तेमाल करके टास्क शेड्यूल करना
मान लीजिए कि समाचार ऐप्लिकेशन के लिए, एक और नई शर्त जोड़ी गई है: ऐप्लिकेशन को उपयोगकर्ता को नियमित रूप से और अपने-आप ताज़ा खबरें फ़ेच करने का विकल्प दें जब तक डिवाइस चार्ज हो रहा है और बिना डेटा वाले नेटवर्क से कनेक्ट है. इससे यह कारोबार की ओर से किया जाने वाला ऑपरेशन है. इस ज़रूरी शर्त को पूरा करने के बाद, अगर उपयोगकर्ता के ऐप्लिकेशन खोलने पर डिवाइस में कनेक्टिविटी नहीं है, तो उपयोगकर्ता हाल की खबरें अब भी देखी जा सकती हैं.
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()
}
}
इस तरह के टास्क के लिए, कारोबारी लॉजिक को अपनी क्लास में शामिल करना चाहिए और उसे एक अलग डेटा सोर्स के तौर पर माना जाता है. इसके बाद, WorkManager सिर्फ़ यह पक्का करने के लिए कि सभी कंस्ट्रेंट होने पर, बैकग्राउंड थ्रेड पर काम एक्ज़ीक्यूट हो लक्ष्य तय होते हैं. इस पैटर्न का इस्तेमाल करके, लागू करने की प्रोसेस को तेज़ी से बदला जा सकता है जिसमें ज़रूरत के हिसाब से बदलाव किए जा सकते हैं.
इस उदाहरण में, समाचार से जुड़े इस टास्क को 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
. सभी टास्क से जुड़े
किसी खास तरह का डेटा उसी क्लास में शामिल किया जाना चाहिए.
अगर ऐप्लिकेशन शुरू होने पर टास्क को ट्रिगर करना ज़रूरी है, तो हमारा सुझाव है कि आप
ऐप्लिकेशन स्टार्टअप का इस्तेमाल करके, WorkManager अनुरोध
लाइब्रेरी को कॉल करता है.
Initializer
.
WorkManager API के साथ काम करने के बारे में ज़्यादा जानने के लिए, WorkManager देखें गाइड देखें.
टेस्ट करना
डिपेंडेंसी इंजेक्शन के सबसे सही तरीके इन मामलों में मदद करते हैं ऐप्लिकेशन को टेस्ट करना. साथ ही, उन क्लास के लिए इंटरफ़ेस का इस्तेमाल करना भी बाहरी संसाधनों से संपर्क करने में मदद मिलती है. किसी यूनिट की जांच करके, नकली यूनिट को इंजेक्ट किया जा सकता है वर्शन होने की ज़रूरी शर्तें पूरी करते हैं. इससे टेस्ट डिटरमिनिस्टिक और भरोसेमंद बन जाता है.
यूनिट टेस्ट
टेस्टिंग के सामान्य दिशा-निर्देश, डेटा की जांच करते समय लागू होते हैं लेयर. यूनिट टेस्ट के लिए, ज़रूरत पड़ने पर असली ऑब्जेक्ट का इस्तेमाल करें और नकली डिपेंडेंसी का इस्तेमाल करें जो बाहरी सोर्स तक पहुंचते हैं. जैसे, किसी फ़ाइल से पढ़ना या नेटवर्क.
इंटिग्रेशन की जांच
बाहरी सोर्स को ऐक्सेस करने वाले इंटिग्रेशन टेस्ट आम तौर पर कम तय होते हैं ऐसा इसलिए है, क्योंकि उन्हें किसी असल डिवाइस पर चलाना होता है. हमारा सुझाव है कि आप ये इंटिग्रेशन, एक कंट्रोल एनवायरमेंट के तहत किए जाते हैं. इससे इंटिग्रेशन टेस्ट और हो सकता है भरोसेमंद.
डेटाबेस के लिए, रूम की मदद से, मेमोरी में मौजूद डेटाबेस बनाया जा सकता है. इस डेटाबेस को पूरी तरह से अलग-अलग कंट्रोल के बारे में जानते हैं. ज़्यादा जानने के लिए, अपने दस्तावेज़ की जांच करें और डेटाबेस पेज पर जाएं.
नेटवर्किंग के लिए, कुछ लोकप्रिय लाइब्रेरी इस तरह से हैं WireMock या मॉकवेबसर्वर जो आपको नकली एचटीटीपी और एचटीटीपीएस कॉल करने देती है. साथ ही, यह पुष्टि करती है कि अनुरोध इस तरह से किए गए हैं उम्मीद है.
सैंपल
Google के इन सैंपल में, डेटा लेयर के इस्तेमाल के बारे में बताया गया है. इस दिशा-निर्देश को देखने के लिए, उन्हें एक्सप्लोर करें:
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर लिंक टेक्स्ट दिखता है
- डोमेन लेयर
- ऑफ़लाइन होने पर इस्तेमाल करने के लिए ऐप्लिकेशन बनाना
- यूज़र इंटरफ़ेस (यूआई) स्टेट प्रोडक्शन