Kriptofolio ऐप सीरीज़ - भाग 4
डिपेंडेंसी इंजेक्शन आपके कोड को काफी बेहतर बनाएगा। यह आपके कोड को अधिक मॉड्यूलर, फ्लेक्सिबल और टेस्टेबल बनाता है। वास्तव में इसका नाम इसके पीछे के विचार से अधिक जटिल लगता है।
सीरीज़ के इस भाग में हम डिपेंडेंसी इंजेक्शन के बारे में सीखेंगे। फिर हम इसे "Kriptofolio" (पहले "My Crypto Coins") ऐप में इम्प्लीमेंट करेंगे। हम Dagger 2 का उपयोग करने जा रहे हैं। Dagger 2 Android के लिए सबसे लोकप्रिय ओपन-सोर्स डिपेंडेंसी इंजेक्शन फ्रेमवर्क है। यह आधुनिक ऐप्स बनाने के लिए एक मूल्यवान कौशल है, भले ही लर्निंग कर्व काफी कठिन हो।
सीरीज़ कंटेंट
- परिचय: 2018-2019 में एक आधुनिक Android ऐप बनाने का रोडमैप
- भाग 1: SOLID सिद्धांतों का परिचय
- भाग 2: अपना Android ऐप बनाना शुरू कैसे करें: मॉकअप्स, UI, और XML लेआउट्स बनाना
- भाग 3: आर्किटेक्चर के बारे में सब कुछ: विभिन्न आर्किटेक्चर पैटर्न्स की खोज और उन्हें अपने ऐप में कैसे उपयोग करें
- भाग 4: Dagger 2 के साथ अपने ऐप में डिपेंडेंसी इंजेक्शन कैसे इम्प्लीमेंट करें (आप यहाँ हैं)
- भाग 5: Retrofit, OkHttp, Gson, Glide और Coroutines का उपयोग करके RESTful वेब सर्विसेज को हैंडल करना
डिपेंडेंसी इंजेक्शन क्या है?
डिपेंडेंसी इंजेक्शन समझाने के लिए पहले हमें यह समझना होगा कि प्रोग्रामिंग में डिपेंडेंसी का क्या अर्थ है। डिपेंडेंसी तब होती है जब ऑब्जेक्ट्स में से एक दूसरे ऑब्जेक्ट के ठोस इम्प्लीमेंटेशन पर निर्भर करता है। आप अपने कोड में डिपेंडेंसी की पहचान तब कर सकते हैं जब आप एक ऑब्जेक्ट को दूसरे के अंदर इंस्टेंटिएट करते हैं। आइए एक व्यावहारिक उदाहरण देखें।
class MyAppClass() { private val library: MyLibrary = MyLibrary(true) ... } class MyLibrary(private val useSpecialFeature: Boolean) { ... }इस उदाहरण से आप देख सकते हैं कि आपकी क्लास MyAppClass सीधे आपकी लाइब्रेरी क्लास MyLibrary के ठोस कॉन्फ़िगरेशन और इम्प्लीमेंटेशन पर निर्भर करेगी। क्या होगा अगर आप किसी दिन थर्ड-पार्टी लाइब्रेरी का उपयोग करना चाहें? क्या होगा अगर आप एक और क्लास चाहते हैं जहां आप बिल्कुल वही लाइब्रेरी कॉन्फ़िगरेशन उपयोग करना चाहते हैं? हर बार आपको अपने कोड में खोजना होगा, सटीक स्थान ढूंढना होगा और उसे बदलना होगा। ये सिर्फ कुछ उदाहरण हैं।
विचार यह है कि ऐप्लिकेशन के कंपोनेंट्स के बीच यह टाइट कपलिंग आपके विकास कार्य को कठिन बना देगा जैसे-जैसे आपका प्रोजेक्ट बढ़ता है। किसी भी समस्या से बचने के लिए, आइए वर्णित कपलिंग को ढीला करने के लिए डिपेंडेंसी इंजेक्शन का उपयोग करें।
class MyAppClass(private val library: MyLibrary) { ... } class MyLibrary(private val useSpecialFeature: Boolean) { ... }बस इतना ही, यह डिपेंडेंसी इंजेक्शन का एक बहुत ही प्रिमिटिव उदाहरण है। अपनी क्लास MyAppClass के अंदर MyLibrary क्लास का एक नया ऑब्जेक्ट बनाने और कॉन्फ़िगर करने के बजाय, आप बस इसे कंस्ट्रक्टर में पास या इंजेक्ट करते हैं। तो MyAppClass MyLibrary के लिए पूरी तरह से गैर-जिम्मेदार हो सकता है।
Dagger 2 क्या है?
Dagger Java और Android दोनों के लिए एक पूरी तरह से स्टैटिक, कंपाइल-टाइम, ओपन-सोर्स डिपेंडेंसी इंजेक्शन फ्रेमवर्क है। इस लेख में मैं इसके दूसरे संस्करण के बारे में बात करूंगा जिसे Google मेंटेन करता है। Square ने इसका पहला संस्करण बनाया था।
Dagger 2 को अब तक बने सबसे कुशल डिपेंडेंसी इंजेक्शन फ्रेमवर्क में से एक माना जाता है। वास्तव में यदि आप Dagger 1, Dagger 2 और Dagger 2.10 की तुलना करते हैं तो आप पाएंगे कि प्रत्येक इम्प्लीमेंटेशन अलग है। आपको हर बार इसे फिर से सीखना होगा क्योंकि लेखकों द्वारा महत्वपूर्ण परिवर्तन किए गए थे। इस लेख को लिखते समय मैं Dagger 2.16 संस्करण का उपयोग कर रहा हूँ और हम केवल उसी पर ध्यान केंद्रित करेंगे।
जैसा कि अब आप डिपेंडेंसी इंजेक्शन के बारे में समझते हैं, हमारे क्लासेज को डिपेंडेंसीज़ नहीं बनानी चाहिए या उनके पास नहीं होनी चाहिए। इसके बजाय उन्हें बाहर से सब कुछ प्राप्त करने की आवश्यकता है। इसलिए Dagger 2 का उपयोग करते समय, यह फ्रेमवर्क सभी आवश्यक डिपेंडेंसीज़ प्रदान करेगा।
यह हमारे लिए बहुत सारा बॉयलरप्लेट कोड जनरेट करके करता है। वह जनरेटेड कोड पूरी तरह से ट्रेसेबल होगा और उस कोड की नकल करेगा जिसे एक यूज़र हाथ से लिख सकता है। Dagger 2 Java में लिखा गया है और इसके एनोटेशन प्रोसेसर द्वारा जनरेट किया गया कोड भी Java कोड होगा।
हालांकि यह बिना किसी समस्या या संशोधन के Kotlin के साथ काम करता है। याद रखें कि Kotlin Java के साथ पूरी तरह से इंटरऑपरेबल है। यदि समान फ्रेमवर्क्स की तुलना की जाए, तो Dagger 2 कम डायनामिक है। यह रन-टाइम पर रिफ्लेक्शन के साथ काम करने के बजाय कंपाइल टाइम पर काम करता है। इसमें बिल्कुल भी रिफ्लेक्शन उपयोग नहीं होता है। इस सब का मतलब है कि इस फ्रेमवर्क को सेट अप करना और सीखना कठिन होगा। यह कंपाइल-टाइम सेफ्टी के साथ परफॉरमेंस बूस्ट प्रदान करेगा।
टूल्स के बिना मैन्युअल डिपेंडेंसी इंजेक्शन
आपने पिछले भाग में My Crypto Coins ऐप के सोर्स कोड में देखा होगा कि बिना किसी डिपेंडेंसी इंजेक्शन टूल का उपयोग किए ऑब्जेक्ट्स इंजेक्ट करने के लिए कोड का एक टुकड़ा है। यह ठीक काम करता है, और यह समाधान इस तरह के छोटे ऐप के लिए काफी अच्छा होगा। यूटिलिटीज पैकेज पर एक नज़र डालें:
/** * विभिन्न Activities और Fragments के लिए आवश्यक क्लासेज को इंजेक्ट करने के लिए उपयोग की जाने वाली स्टैटिक मेथड्स। */ object InjectorUtils { private fun getCryptocurrencyRepository(context: Context): CryptocurrencyRepository { return CryptocurrencyRepository.getInstance( AppDatabase.getInstance(context).cryptocurrencyDao()) } fun provideMainViewModelFactory( application: Application ): MainViewModelFactory { val repository = getCryptocurrencyRepository(application) return MainViewModelFactory(application, repository) } fun provideAddSearchViewModelFactory( context: Context ): AddSearchViewModelFactory { val repository = getCryptocurrencyRepository(context) return AddSearchViewModelFactory(repository) } }जैसा कि आप देख सकते हैं यह क्लास सारा काम करेगी। यह उन एक्टिविटीज़ या फ्रैगमेंट्स के लिए ViewModel फैक्ट्रीज़ बनाएगी जिनकी उन्हें आवश्यकता है।
/** * [CryptocurrencyRepository] लेने वाले कंस्ट्रक्टर के साथ [MainViewModel] बनाने के लिए फैक्ट्री। */ class MainViewModelFactory(private val application: Application, private val repository: CryptocurrencyRepository) : ViewModelProvider.NewInstanceFactory() { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel?> create(modelClass: Class<T>): T { return MainViewModel(application, repository) as T } }फिर आप InjectorUtils क्लास का उपयोग वहीं करते हैं जहां आपको एक विशिष्ट ViewModel फैक्ट्री प्राप्त करने की आवश्यकता होती है:
/** * एक प्लेसहोल्डर फ्रैगमेंट जिसमें एक सिंपल व्यू होता है। */ class MainListFragment : Fragment() { ... private lateinit var viewModel: MainViewModel ... override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) setupList() ... } ... private fun subscribeUi(activity: FragmentActivity) { // यह Dagger का उपयोग करने से पहले कोड इंजेक्ट करने का पुराना तरीका था। val factory = InjectorUtils.provideMainViewModelFactory(activity.application) // पैरेंट एक्टिविटी को LifecycleOwner के रूप में उपयोग करके ViewModelProviders से ViewModel प्राप्त करें। viewModel = ViewModelProviders.of(activity, factory).get(MainViewModel::class.java) ... } }जैसा कि आप देख सकते हैं हमारा MainListFragment क्लास CryptocurrencyRepository या AppDatabase के बारे में भी नहीं जानता। यह InjectorUtils क्लास से एक सफलतापूर्वक निर्मित फैक्ट्री प्राप्त करता है। वास्तव में यह इसे करने का एक सरल तरीका है। हम इससे छुटकारा पाने जा रहे हैं और सीखेंगे कि My Crypto Coins ऐप सोर्स कोड में उन्नत डिपेंडेंसी इंजेक्शन के लिए Dagger 2 टूल कैसे सेटअप करें। यदि यह ऐप कार्यक्षमता और कोड में विस्तारित होता, तो मुझे कोई संदेह नहीं कि हम एक प्रोफेशनल डिपेंडेंसी इंजेक्शन फ्रेमवर्क का उपयोग करने के लाभों को मैन्युअल समाधान पर बहुत तेजी से देखना शुरू कर देंगे।
तो आइए अभी InjectorUtils क्लास को डिलीट करें और सीखें कि My Crypto Coins ऐप प्रोजेक्ट में Dagger 2 कैसे सेटअप करें।
Kotlin के साथ MVVM के लिए डिपेंडेंसी इंजेक्शन
ViewModels, Activities और Fragments के साथ Dagger 2 कैसे सेटअप करें
अब हम My Crypto Coins ऐप प्रोजेक्ट पर Dagger 2 स्टेप-बाय-स्टेप सेटअप देखेंगे।
शुरू करने के लिए, आपको Kotlin के अपने एनोटेशन प्रोसेसिंग टूल (kapt) को इनेबल करना चाहिए। फिर विशेष Dagger 2 डिपेंडेंसीज़ जोड़ें।
आप इसे अपने gradle फ़ाइल में ये लाइनें जोड़कर कर सकते हैं:
apply plugin: 'kotlin-kapt' // एनोटेशन प्रोसेसिंग के लिए ... implementation "com.google.dagger:dagger:$versions.dagger" implementation "com.google.dagger:dagger-android:$versions.dagger" implementation "com.google.dagger:dagger-android-support:$versions.dagger" kapt "com.google.dagger:dagger-compiler:$versions.dagger" kapt "com.google.dagger:dagger-android-processor:$versions.dagger"Kapt प्लगइन कंपाइलर को Java और Kotlin के बीच इंटरऑपरेबिलिटी के लिए आवश्यक स्टब क्लासेज जनरेट करने में सक्षम बनाता है। सुविधा के लिए हम ठोस Dagger 2 संस्करण को एक अलग gradle फ़ाइल में परिभाषित करेंगे, जैसा कि हम अपने सभी डिपेंडेंसीज़ के साथ करते हैं।
def versions = [:] versions.dagger = "2.16" ext.versions = versionsउपलब्ध नवीनतम संस्करण खोजने के लिए Github पर Dagger 2 के आधिकारिक रिपॉजिटरी में रिलीज़ देखें।
अब, अपना एप्लिकेशन App क्लास बनाएं।
इसे छोड़ दें यदि आपने पहले से ही यह क्लास सेट कर ली है। इसके पूरा होने के बाद, हम इसे अभी के लिए वैसे ही छोड़ देंगे, लेकिन बाद में इस पर वापस आएंगे।
class App : Application() { override fun onCreate() { super.onCreate() } }My Crypto Coins ऐप के लिए, हमने पहले ही एप्लिकेशन क्लास पहले बनाई थी।
अब, अपने मैनिफेस्ट फ़ाइल को अपडेट करें ताकि आपकी App क्लास इनेबल हो सके।
इसे छोड़ दें यदि आपने पहले ही ऐसा कर लिया है।
<manifest xmlns:android="https://schemas.android.com/apk/res/android" package="com.baruckis.mycryptocoins"> <application android:name=".App" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" ...My Crypto Coins ऐप के लिए, हमने App क्लास को मैनिफेस्ट में पहले ही सेट कर दिया था।
अब एक नया पैकेज dependencyinjection बनाएं।
यहां हम Dagger इम्प्लीमेंटेशन से संबंधित सभी फाइलें रखेंगे।
AppModule क्लास मॉड्यूल बनाएं जो आपके पूरे ऐप्लिकेशन में डिपेंडेंसीज़ प्रदान करेगा।
/** * AppModule ऐप के एक भाग के लिए ऐप-वाइड डिपेंडेंसीज़ प्रदान करेगा। * इसे हमारे ऐप्लिकेशन में उपयोग किए जाने वाले ऑब्जेक्ट्स को इनिशियलाइज़ करना चाहिए, जैसे Room डेटाबेस, Retrofit, Shared Preference, आदि। */ @Module(includes = [ViewModelsModule::class]) class AppModule() { @Singleton // एनोटेशन Dagger कंपाइलर को सूचित करता है कि इंस्टेंस पूरे ऐप्लिकेशन लाइफसाइकिल में केवल एक बार बनाया जाना चाहिए। @Provides // एनोटेशन Dagger कंपाइलर को सूचित करता है कि यह मेथड Context रिटर्न टाइप के लिए कंस्ट्रक्टर है। fun provideContext(app: App): Context = app // provide को प्रीफिक्स के रूप में उपयोग करना एक सामान्य कन्वेंशन है लेकिन आवश्यकता नहीं। @Singleton @Provides fun provideCryptocurrencyRepository(context: Context): CryptocurrencyRepository { return CryptocurrencyRepository.getInstance(AppDatabase.getInstance(context).cryptocurrencyDao()) } }जैसा कि आप देख सकते हैं, एक Dagger मॉड्यूल बनाने के लिए हमें इसे विशेष @Module एनोटेशन से एनोटेट करना होगा। प्रोजेक्ट्स में आमतौर पर कई Dagger मॉड्यूल्स होते हैं। उनमें से एक का ऐप-वाइड डिपेंडेंसीज़ प्रदान करना विशिष्ट है। यह AppModule हमारे ऐप्लिकेशन में उपयोग किए जाने वाले ऑब्जेक्ट्स को इनिशियलाइज़ करने के लिए उपयोग किया जाएगा, जैसे Room डेटाबेस, Retrofit, Shared Preference, आदि।
एक उदाहरण के रूप में, हम AppModule द्वारा Context ऑब्जेक्ट प्रदान करने के एक बहुत ही सामान्य परिदृश्य पर चर्चा कर सकते हैं, यदि हमें इसे ऐप में कहीं भी एक्सेस करने की आवश्यकता है। आइए कोड का विश्लेषण करें कि यह कैसे करना है।
हमें एक विशेष Dagger एनोटेशन @Provides का उपयोग करने की आवश्यकता है। यह Dagger को बताता है कि मेथड एक विशिष्ट प्रकार की डिपेंडेंसी प्रदान करता है, हमारे मामले में, एक Context ऑब्जेक्ट। इसलिए जब ऐप में कहीं हम Context इंजेक्ट करने का अनुरोध करते हैं, तो AppModule वह स्थान है जहां Dagger इसे ढूंढता है। और हमारे मेथड्स के नाम कोई मायने नहीं रखते, क्योंकि Dagger केवल रिटर्न टाइप की परवाह करता है। मेथड का नाम provide प्रीफिक्स के साथ रखना केवल सामान्य प्रथा है, लेकिन यह कुछ भी हो सकता है जो आप चाहते हैं।
उसी मेथड पर लागू @Singleton एनोटेशन Dagger एनोटेशन्स का हिस्सा नहीं है। यह javax पैकेज के अंदर समाहित है। यह एनोटेशन Dagger को बताता है कि उस डिपेंडेंसी का केवल एक ही इंस्टेंस होना चाहिए।
आपको यह जांचने के लिए बॉयलरप्लेट कोड लिखने की आवश्यकता नहीं है कि ऑब्जेक्ट का कोई अन्य इंस्टेंस पहले से उपलब्ध है या नहीं। कोड जनरेट करते समय Dagger इस एनोटेशन की वजह से वह सारा लॉजिक आपके लिए संभालेगा। ध्यान दें कि हमारा AppModule एक अन्य मॉड्यूल ViewModelsModule को शामिल करता है। आइए अब इसे बनाएं।
ViewModelsModule क्लास मॉड्यूल बनाएं। यह मॉड्यूल आपके पूरे ऐप्लिकेशन में ViewModels प्रदान करने के लिए जिम्मेदार होगा।
/** * ViewModels प्रदान करने के लिए जिम्मेदार होगा। */ @Module abstract class ViewModelsModule { // हम ViewModel क्लास के इस इम्प्लीमेंटेशन को लेना चाहते हैं और इसे MainViewModel::class को उस मैप की कुंजी के रूप में एक इंजेक्टेबल मैप में उपलब्ध कराना चाहते हैं। @Binds @IntoMap @ViewModelKey(MainViewModel::class) // हम @ViewModelKey एनोटेशन के साथ परिभाषित मल्टीबाउंड मैप पर एक प्रतिबंध का उपयोग करते हैं, और यदि हमें किसी की आवश्यकता नहीं है, तो हमें Dagger द्वारा प्रदान किए गए @ClassKey एनोटेशन का उपयोग करना चाहिए। abstract fun bindMainViewModel(mainViewModel: MainViewModel): ViewModel @Binds @IntoMap @ViewModelKey(AddSearchViewModel::class) abstract fun bindAddSearchViewModel(addSearchViewModel: AddSearchViewModel): ViewModel @Binds abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory }यह मॉड्यूल Dagger 2 फीचर मैप मल्टी बाइंडिंग्स का उपयोग करता है। इसका उपयोग करके, हम अपनी पसंद के ऑब्जेक्ट्स को एक मैप में योगदान करते हैं जो हमारे ऐप में कहीं भी इंजेक्टेबल बन जाता है। Dagger एनोटेशन्स @Binds, @IntoMap और हमारे कस्टम एनोटेशन @ViewModelKey (इसे हम बनाने जा रहे हैं) के संयोजन का उपयोग करके, हम अपने मैप के अंदर कुंजी MainViewModel::class और मान MainViewModel इंस्टेंस के साथ एक एंट्री बनाते हैं। हम कुछ सामान्य ViewModelFactory क्लास की मदद से विशिष्ट फैक्ट्री को बाइंड करते हैं। हमें यह क्लास बनाने की आवश्यकता है।
कस्टम एनोटेशन क्लास ViewModelKey बनाएं।
/** * एक एनोटेशन क्लास जो Dagger को बताती है कि इसका उपयोग मल्टी बाउंड मैप्स में कुंजियों को निर्धारित करने के लिए किया जा सकता है। */ @MustBeDocumented @Target( AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER ) @Retention(AnnotationRetention.RUNTIME) @MapKey annotation class ViewModelKey(val value: KClass<out ViewModel>) // हम केवल उन क्लासेज का उपयोग कर सकते हैं जो ViewModel से इनहेरिट करती हैं।यह क्लास ViewModelsModule में ViewModels को बाइंड करने के लिए उपयोग की जाती है। विशिष्ट एनोटेशन @ViewModelKey हमारे मैप की कुंजी को दर्शाता है। हमारी कुंजी केवल एक क्लास हो सकती है जो ViewModel से इनहेरिट करती है।
ViewModelFactory क्लास बनाएं।
/** * क्लास टू प्रोवाइडर मैप को ऑटो-जनरेट करने के लिए फैक्ट्री। * हम बाद में इंजेक्टेबल ऑब्जेक्ट बनाने के लिए Provider<T> का उपयोग करते हैं। */ @Suppress("UNCHECKED_CAST") @Singleton class ViewModelFactory @Inject constructor(private val viewModelsMap: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { var creator: Provider<out ViewModel>? = viewModelsMap[modelClass] if (creator == null) { for (entry in viewModelsMap.entries) { if (modelClass.isAssignableFrom(entry.key)) { creator = entry.value break } } } if (creator == null) { throw IllegalArgumentException("Unknown model class $modelClass") } try { return creator.get() as T } catch (e: Exception) { throw RuntimeException(e) } } }यह ViewModelFactory एक यूटिलिटी क्लास है जो आपको डायनामिकली ViewModels बनाने में मदद करती है। यहां आप आर्गुमेंट के रूप में जनरेटेड मैप प्रदान करते हैं। create() मेथड मैप से सही इंस्टेंस चुनने में सक्षम होगा।
ActivityBuildersModule क्लास मॉड्यूल बनाएं।
/** * Dagger @Inject का उपयोग करने के लिए अभिप्रेत सभी एक्टिविटीज़ को यहां सूचीबद्ध किया जाना चाहिए। */ @Module abstract class ActivityBuildersModule { @ContributesAndroidInjector(modules = [MainListFragmetBuildersModule::class]) // जहां इंजेक्शन लागू करना है। abstract fun contributeMainActivity(): MainActivity @ContributesAndroidInjector abstract fun contributeAddSearchActivity(): AddSearchActivity }यह मॉड्यूल आपकी सभी एक्टिविटीज़ के निर्माण के लिए जिम्मेदार है। यह इस क्लास में परिभाषित सभी Activities के लिए AndroidInjector जनरेट करेगा। फिर ऑब्जेक्ट्स को एक्टिविटीज़ में AndroidInjection.inject(this) का उपयोग करके एक्टिविटी लाइफसाइकिल से onCreate फंक्शन में इंजेक्ट किया जा सकता है। ध्यान दें कि यह मॉड्यूल फ्रैगमेंट्स के लिए जिम्मेदार एक अन्य अलग मॉड्यूल का भी उपयोग करता है। हम अगले इस मॉड्यूल को बनाएंगे।
MainListFragmetBuildersModule क्लास मॉड्यूल बनाएं।
/** * MainActivity से संबंधित सभी फ्रैगमेंट्स जो Dagger @Inject का उपयोग करने के लिए अभिप्रेत हैं, उन्हें यहां सूचीबद्ध किया जाना चाहिए। */ @Module abstract class MainListFragmetBuildersModule { @ContributesAndroidInjector() // फ्रैगमेंट को Dagger ग्राफ से अटैच करता है। abstract fun contributeMainListFragment(): MainListFragment }यह मॉड्यूल MainActivity से संबंधित आपके सभी फ्रैगमेंट्स का निर्माण करेगा। यह इस क्लास में परिभाषित सभी फ्रैगमेंट्स के लिए AndroidInjector जनरेट करेगा। ऑब्जेक्ट्स को फ्रैगमेंट्स में AndroidSupportInjection.inject(this) का उपयोग करके फ्रैगमेंट लाइफसाइकिल से onAttach फंक्शन में इंजेक्ट किया जा सकता है।
AppComponent क्लास कंपोनेंट बनाएं।
/** * ऐप के लिए सिंगलटन कंपोनेंट इंटरफेस। यह सभी मॉड्यूल्स को एक साथ बांधता है। * कंपोनेंट का उपयोग ऑब्जेक्ट्स को उनकी डिपेंडेंसीज़ से जोड़ने के लिए किया जाता है। * Dagger DaggerAppComponent को ऑटो-जनरेट करेगा जिसका उपयोग Application पर इनिशियलाइज़ेशन के लिए किया जाता है। */ @Singleton @Component( modules = [ // AndroidSupportInjectionModule Dagger की एक क्लास है और हमें इसे बनाने की आवश्यकता नहीं है। // यदि आप फ्रैगमेंट में इंजेक्शन का उपयोग करना चाहते हैं तो AndroidSupportInjectionModule.class का उपयोग करें अन्यथा AndroidInjectionModule का उपयोग करें। AndroidSupportInjectionModule::class, AppModule::class, ActivityBuildersModule::class ] ) interface AppComponent { @Component.Builder // कंपोनेंट के इंस्टेंटिएशन के लिए उपयोग किया जाता है। interface Builder { @BindsInstance // हमारे ऐप्लिकेशन इंस्टेंस को हमारे Dagger ग्राफ से बाइंड करता है। fun application(application: App): Builder fun build(): AppComponent } // वह ऐप्लिकेशन जिसे मॉड्यूल्स द्वारा घोषित डिपेंडेंसीज़ का अनुरोध करने की अनुमति है // (@Inject एनोटेशन के माध्यम से) उसे यहां व्यक्तिगत inject() मेथड्स के साथ घोषित किया जाना चाहिए। fun inject(app: App) }कंपोनेंट एक बहुत महत्वपूर्ण क्लास है। यह उपरोक्त सभी को एक साथ काम करने में सक्षम बनाता है। यह ऑब्जेक्ट्स को उनकी डिपेंडेंसीज़ से जोड़कर ऐसा करता है। Dagger डिपेंडेंसी इंजेक्शन करने के लिए आवश्यक कोड जनरेट करने के लिए इस इंटरफेस का उपयोग करेगा।
कंपोनेंट क्लास बनाने के लिए आपको Dagger एनोटेशन @Component का उपयोग करना होगा। यह मॉड्यूल्स की एक सूची को इनपुट के रूप में लेता है। एक अन्य एनोटेशन @Component.Builder हमें कुछ इंस्टेंस को कंपोनेंट से बाइंड करने की अनुमति देता है।
फिर एक ग्राफ ऑब्जेक्ट जनरेट करें।
इस समय आपके पास अपने सभी मॉड्यूल्स और आपका कंपोनेंट सेटअप है। आप अपने Android Studio IDE में Build -> Make Module सिलेक्ट करके अपना ग्राफ ऑब्जेक्ट जनरेट कर सकते हैं। भविष्य के चरणों के लिए हमें इस जनरेशन की आवश्यकता होगी।
अब एक Injectable इंटरफेस बनाएं।
/** * यह सिर्फ एक सादा खाली मार्कर इंटरफेस है, जो बताता है कि यदि वे इसे इम्प्लीमेंट करते हैं तो एक्टिविटीज़ या फ्रैगमेंट्स को ऑटोमेटिकली इंजेक्ट करें। */ interface Injectableयह भविष्य के चरणों के लिए भी आवश्यक होगा। Injectable इंटरफेस को एक्टिविटीज़ या फ्रैगमेंट्स द्वारा इम्प्लीमेंट किया जाना चाहिए जिन्हें हम ऑटोमेटिकली इंजेक्टेबल बनाना चाहते हैं।
AppInjector नाम से एक नया हेल्पर क्लास बनाएं।
/** * प्रत्येक एक्टिविटी या फ्रैगमेंट पर inject मेथड कॉल करने से बचने के लिए यह एक सिंपल हेल्पर क्लास है। */ object AppInjector { fun init(app: App) { // यहां हम Dagger को इनिशियलाइज़ करते हैं। DaggerAppComponent AppComponent से ऑटो-जनरेटेड है। DaggerAppComponent.builder().application(app).build().inject(app) app.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks { override fun onActivityPaused(activity: Activity) { } override fun onActivityResumed(activity: Activity) { } override fun onActivityStarted(activity: Activity) { } override fun onActivityDestroyed(activity: Activity) { } override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle?) { } override fun onActivityStopped(activity: Activity) { } override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { handleActivity(activity) } }) } private fun handleActivity(activity: Activity) { if (activity is HasSupportFragmentInjector || activity is Injectable) { // inject() मेथड कॉल करने से Dagger डिपेंडेंसी ग्राफ में सिंगलटन्स का पता लगाकर मिलान करने वाले रिटर्न टाइप को खोजने की कोशिश करेगा। // यदि यह एक ढूंढता है, तो यह संबंधित फील्ड्स को रेफरेंस असाइन करता है। AndroidInjection.inject(activity) } if (activity is FragmentActivity) { activity.supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentCreated(fragmentManager: FragmentManager, fragment: Fragment, savedInstanceState: Bundle?) { if (fragment is Injectable) { AndroidSupportInjection.inject(fragment) } } }, true) } } }यह प्रत्येक एक्टिविटी या फ्रैगमेंट पर inject मेथड कॉल करने से बचने के लिए सिर्फ एक सिंपल हेल्पर क्लास है।
अब, App क्लास को सेटअप करें जिसे हमने पहले ही बनाया था।
class App : Application(), HasActivityInjector { @Inject // यह एक टाइप के लिए उपयुक्त इंजेक्टर फैक्ट्री खोजने की Dagger मशीनरी को इम्प्लीमेंट करता है। lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity> override fun onCreate() { super.onCreate() // ऑटोमेटिकली एक्टिविटीज़ और फ्रैगमेंट्स को इंजेक्ट करने के लिए इनिशियलाइज़ करें यदि वे Injectable इंटरफेस इम्प्लीमेंट करते हैं। AppInjector.init(this) ... } // HasActivityInjector इंटरफेस द्वारा आवश्यक है ताकि Activity के लिए Dagger सेटअप हो सके। override fun activityInjector(): AndroidInjector<Activity> = dispatchingAndroidInjector }क्योंकि ऐप्लिकेशन में एक्टिविटीज़ हैं, हमें HasActivityInjector इंटरफेस इम्प्लीमेंट करने की आवश्यकता है। यदि आप Android Studio द्वारा DaggerAppComponent पर कोई एरर देखते हैं, तो ऐसा इसलिए है क्योंकि आपने पिछले चरण में बताए अनुसार एक नई फ़ाइल जनरेट नहीं की है।
तो, MainActivity को मेन ViewModel फैक्ट्री इंजेक्ट करने और फ्रैगमेंट इंजेक्शन के लिए सपोर्ट जोड़ने के लिए सेटअप करें।
// इस एक्टिविटी से संबंधित फ्रैगमेंट्स को इंजेक्ट करने का सपोर्ट करने के लिए हमें HasSupportFragmentInjector इम्प्लीमेंट करने की आवश्यकता है। // हमें इसे इम्प्लीमेंट करने की आवश्यकता नहीं होती, यदि हमारी एक्टिविटी में कोई फ्रैगमेंट नहीं होते या फ्रैगमेंट्स को कुछ भी इंजेक्ट करने की आवश्यकता नहीं होती। class MainActivity : AppCompatActivity(), HasSupportFragmentInjector { @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment> @Inject lateinit var viewModelFactory: ViewModelProvider.Factory private lateinit var mainViewModel: MainViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // इस एक्टिविटी को LifecycleOwner के रूप में उपयोग करके ViewModelProviders से ViewModel प्राप्त करें। mainViewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java) ... } ... override fun supportFragmentInjector(): AndroidInjector<Fragment> = dispatchingAndroidInjector ... }क्योंकि हमारी एक्टिविटीज़ में चाइल्ड फ्रैगमेंट्स हैं, हमें HasSupportFragmentInjector इंटरफेस इम्प्लीमेंट करने की आवश्यकता है। हमें इसकी भी आवश्यकता है क्योंकि हम अपने फ्रैगमेंट्स में इंजेक्शन करने की योजना बना रहे हैं। हमारी एक्टिविटी को यह नहीं पता होना चाहिए कि यह कैसे इंजेक्ट होती है। हम onCreate() मेथड को ओवरराइड करने के अंदर AndroidInjection.inject(this) कोड लाइन का उपयोग करते हैं।
inject() मेथड कॉल करने से Dagger 2 डिपेंडेंसी ग्राफ में सिंगलटन्स का पता लगाकर मिलान करने वाले रिटर्न टाइप को खोजने की कोशिश करेगा। हालांकि हमें यहां कोई कोड लिखने की आवश्यकता नहीं है क्योंकि यह पहले से बनाए गए AppInjector हेल्पर क्लास द्वारा हमारे लिए किया जाता है जिसे हमने अपनी ऐप्लिकेशन क्लास के अंदर इनिशियलाइज़ किया था।
फिर, मेन ViewModel फैक्ट्री इंजेक्ट करने के लिए MainListFragment सेटअप करें।
/** * एक प्लेसहोल्डर फ्रैगमेंट जिसमें एक सिंपल व्यू होता है। */ class MainListFragment : Fragment(), Injectable { ... @Inject lateinit var viewModelFactory: ViewModelProvider.Factory private lateinit var viewModel: MainViewModel ... override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) ... subscribeUi(activity!!) } ... private fun subscribeUi(activity: FragmentActivity) { // पैरेंट एक्टिविटी को LifecycleOwner के रूप में उपयोग करके ViewModelProviders से ViewModel प्राप्त करें। viewModel = ViewModelProviders.of(activity, viewModelFactory).get(MainViewModel::class.java) ... } }एक्टिविटीज़ के समान, यदि हम चाहते हैं कि हमारा फ्रैगमेंट इंजेक्टेबल हो, तो उसके onAttach मेथड में हमें कोड AndroidSupportInjection.inject(this) लिखना चाहिए। लेकिन फिर से यह काम पहले से बनाए गए AppInjector हेल्पर द्वारा किया जाता है, इसलिए हम इसे छोड़ सकते हैं। बस ध्यान दें कि हमें पहले बनाए गए Injectable इंटरफेस को हेल्पर के काम करने के लिए जोड़ने की आवश्यकता है।
बधाई हो, हमने My Crypto Coins ऐप प्रोजेक्ट में Dagger 2 इम्प्लीमेंट कर लिया है। बेशक यह लेख आपके ऐप में Dagger 2 को तुरंत डिप्लॉय करने के लिए एक क्विक गाइड है, लेकिन इसकी गहरी कवरेज नहीं है। यदि आप बेसिक्स पर खोया हुआ महसूस करते हैं तो मैं आपको इस विषय पर शोध जारी रखने की सलाह देता हूं।
रिपॉजिटरी
GitHub पर अपडेटेड "Kriptofolio" (पहले "My Crypto Coins") ऐप का सोर्स कोड देखें।
GitHub पर सोर्स देखें
Ačiū! पढ़ने के लिए धन्यवाद! मैंने मूल रूप से यह पोस्ट अपने पर्सनल ब्लॉग www.baruckis.com पर 7 अक्टूबर, 2018 को प्रकाशित की थी।