क्रिप्टोफोलियो ऐप शृंखला - भाग 4
निर्भरता इंजेक्शन आपके कोड में काफी सुधार करेगा। यह आपके कोड को अधिक मॉड्यूलर, लचीला और परीक्षण योग्य बनाता है। वास्तव में इसका नाम उस विचार से कहीं अधिक जटिल लगता है जो इसके पीछे खड़ा है।
श्रृंखला के इस भाग में हम निर्भरता इंजेक्शन के बारे में जानेंगे। फिर हम इसे "क्रिप्टोफोलियो" (पहले "माई क्रिप्टो कॉइन्स") ऐप में लागू करेंगे। हम डैगर 2 का उपयोग करने जा रहे हैं। डैगर 2 एंड्रॉइड के लिए सबसे लोकप्रिय ओपन-सोर्स डिपेंडेंसी इंजेक्शन फ्रेमवर्क है। आधुनिक ऐप्स बनाने के लिए यह एक मूल्यवान कौशल है, भले ही सीखने की अवस्था काफी कठिन हो।
श्रृंखला सामग्री
- परिचय:2018–2019 में एक आधुनिक Android ऐप बनाने का रोडमैप
- भाग 1:ठोस सिद्धांतों का परिचय
- भाग 2:अपना Android ऐप कैसे बनाना शुरू करें:मॉकअप, UI और XML लेआउट बनाना
- भाग 3:उस आर्किटेक्चर के बारे में सब कुछ:विभिन्न आर्किटेक्चर पैटर्न की खोज करना और उन्हें अपने ऐप में कैसे उपयोग करना है
- भाग 4:डैगर 2 (आप यहां हैं) के साथ अपने ऐप में डिपेंडेंसी इंजेक्शन कैसे लागू करें
- भाग 5:Retrofit, OkHttp, Gson, Glide और Coroutines का उपयोग करके RESTful Web Services को हैंडल करें
डिपेंडेंसी इंजेक्शन क्या है?
डिपेंडेंसी इंजेक्शन की व्याख्या करने के लिए पहले हमें यह समझना होगा कि प्रोग्रामिंग में डिपेंडेंसी का क्या मतलब है। एक निर्भरता तब होती है जब वस्तुओं में से एक किसी अन्य वस्तु के ठोस कार्यान्वयन पर निर्भर करती है। जब भी आप किसी ऑब्जेक्ट को दूसरे के भीतर इंस्टेंट करते हैं तो आप अपने कोड में निर्भरता की पहचान कर सकते हैं। आइए एक व्यावहारिक उदाहरण देखें।
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) {
...
}
बस, यह एक बहुत ही आदिम निर्भरता इंजेक्शन उदाहरण है। नया MyLibrary
बनाने और कॉन्फ़िगर करने के बजाय आपकी कक्षा के अंदर वर्ग वस्तु MyAppClass
, आप बस इसे कंस्ट्रक्टर में पास या इंजेक्ट करें। तो MyAppClass
MyLibrary
. के लिए पूरी तरह से गैर-जिम्मेदार हो सकते हैं ।
डैगर 2 क्या है?
डैगर जावा और एंड्रॉइड दोनों के लिए पूरी तरह से स्थिर, संकलन-समय, ओपन-सोर्स निर्भरता इंजेक्शन ढांचा है। इस लेख में मैं इसके दूसरे संस्करण के बारे में बात करूंगा जिसे Google बनाए रखता है। स्क्वायर ने अपना पुराना संस्करण बनाया।
डैगर 2 को अब तक निर्मित सबसे कुशल निर्भरता इंजेक्शन ढांचे में से एक माना जाता है। वास्तव में यदि आप डैगर 1, डैगर 2 और डैगर 2.10 की तुलना करते हैं तो आप पाएंगे कि प्रत्येक कार्यान्वयन अलग है। आपको इसे हर बार फिर से सीखने की जरूरत है क्योंकि लेखकों द्वारा महत्वपूर्ण परिवर्तन किए गए थे। इस लेख को लिखते समय मैं डैगर 2.16 संस्करण का उपयोग कर रहा हूं और हम केवल इस पर ध्यान केंद्रित करने जा रहे हैं।
जैसा कि अब आप डिपेंडेंसी इंजेक्शन के बारे में समझ गए हैं, हमारी क्लासेस को डिपेंडेंसी नहीं बनानी चाहिए या उनमें डिपेंडेंसी नहीं होनी चाहिए। इसके बजाय उन्हें सब कुछ बाहर से लाने की जरूरत है। इसलिए डैगर 2 का उपयोग करते समय, यह ढांचा सभी आवश्यक निर्भरताएं प्रदान करेगा।
यह हमारे लिए बहुत सारे बॉयलरप्लेट कोड उत्पन्न करके ऐसा करता है। वह उत्पन्न कोड पूरी तरह से पता लगाने योग्य होगा और उस कोड की नकल करेगा जिसे उपयोगकर्ता हाथ से लिख सकता है। डैगर 2 जावा में लिखा गया है और इसके एनोटेशन प्रोसेसर द्वारा उत्पन्न कोड भी जावा कोड होगा।
हालाँकि यह बिना किसी समस्या या संशोधन के कोटलिन के साथ काम करता है। याद रखें कि कोटलिन जावा के साथ पूरी तरह से इंटरऑपरेबल है। यदि समान ढांचे की तुलना में, डैगर 2 कम गतिशील है। यह प्रतिबिंब के साथ रन-टाइम के बजाय संकलन समय पर काम करता है। कोई प्रतिबिंब उपयोग बिल्कुल नहीं है। इसका मतलब यह है कि इस ढांचे को स्थापित करना और सीखना कठिन होगा। यह संकलन-समय सुरक्षा के साथ प्रदर्शन को बढ़ावा देगा।
बिना टूल के मैन्युअल डिपेंडेंसी इंजेक्शन
आपने पिछले भाग से माई क्रिप्टो कॉइन्स ऐप सोर्स कोड में देखा होगा कि किसी भी निर्भरता इंजेक्शन टूल का उपयोग किए बिना वस्तुओं को इंजेक्ट करने के लिए कोड का एक टुकड़ा है। यह ठीक काम करता है, और इस तरह के एक छोटे से ऐप के लिए यह समाधान काफी अच्छा होगा। उपयोगिताओं पैकेज पर एक नज़र डालें:
/**
* Static methods used to inject classes needed for various Activities and 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 फ़ैक्टरियाँ बनाएगा जिनकी उन्हें आवश्यकता है।
/**
* Factory for creating a [MainViewModel] with a constructor that takes a
* [CryptocurrencyRepository].
*/
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
. का उपयोग करें इस तरह की कक्षा जहां आपको एक विशिष्ट व्यूमोडेल फैक्ट्री प्राप्त करने की आवश्यकता है:
/**
* A placeholder fragment containing a simple view.
*/
class MainListFragment : Fragment() {
...
private lateinit var viewModel: MainViewModel
...
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupList()
...
}
...
private fun subscribeUi(activity: FragmentActivity) {
// This is the old way how we were injecting code before using Dagger.
val factory = InjectorUtils.provideMainViewModelFactory(activity.application)
// Obtain ViewModel from ViewModelProviders, using parent activity as LifecycleOwner.
viewModel = ViewModelProviders.of(activity, factory).get(MainViewModel::class.java)
...
}
}
जैसा कि आप हमारा MainListFragment
देखते हैं क्लास को CryptocurrencyRepository
के बारे में भी पता नहीं है या AppDatabase
. इसे इंजेक्टर यूटिल्स क्लास से सफलतापूर्वक निर्मित फैक्ट्री मिलती है। असल में ऐसा करने का यह एक आसान तरीका है। हम इससे छुटकारा पाने जा रहे हैं और सीखेंगे कि उन्नत निर्भरता इंजेक्शन के लिए डैगर 2 टूल कैसे सेट करें। यदि यह ऐप कार्यक्षमता और कोड में विस्तार करेगा, तो मुझे संदेह नहीं है कि हम मैन्युअल समाधान पर एक पेशेवर निर्भरता इंजेक्शन ढांचे का उपयोग करने के लाभों को वास्तव में तेजी से देखना शुरू कर देंगे।
तो चलिए मिटाते हैं InjectorUtils
अभी क्लास करें और माई क्रिप्टो कॉइन्स ऐप सोर्स कोड में डैगर 2 को सेटअप करना सीखें।
कोटलिन के साथ MVVM के लिए डिपेंडेंसी इंजेक्शन
डैगर 2 को ViewModels, गतिविधियों और टुकड़ों के साथ कैसे सेटअप करें
अब हम माई क्रिप्टो कॉइन्स ऐप प्रोजेक्ट पर डैगर 2 स्टेप बाय स्टेप सेटअप से गुजरेंगे।
शुरू करने के लिए, आपको कोटलिन के अपने एनोटेशन प्रोसेसिंग टूल (kapt) को सक्षम करना चाहिए। फिर विशेष डैगर 2 निर्भरताएँ जोड़ें।
आप इन पंक्तियों को अपनी ग्रेडल फ़ाइल में जोड़कर ऐसा कर सकते हैं:
apply plugin: 'kotlin-kapt' // For annotation processing
...
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 प्लगइन कंपाइलर को जावा और कोटलिन के बीच इंटरऑपरेबिलिटी के लिए आवश्यक स्टब क्लास उत्पन्न करने में सक्षम करेगा। सुविधा के लिए हम कंक्रीट डैगर 2 संस्करण को एक अलग ग्रेडल फ़ाइल में परिभाषित करेंगे, जैसा कि हम अपनी सभी निर्भरताओं के साथ करते हैं।
def versions = [:]
versions.dagger = "2.16"
ext.versions = versions
उपलब्ध नवीनतम संस्करण को खोजने के लिए गीथूब पर डैगर 2 के आधिकारिक भंडार में रिलीज की जांच करें।
अब, अपना एप्लिकेशन बनाएं App
कक्षा।
अगर आपके पास पहले से ही यह क्लास सेट है तो इसे छोड़ दें। आपके द्वारा ऐसा करने के बाद, हम इसे कुछ समय के लिए वैसे ही छोड़ देंगे, लेकिन बाद में वापस आ जाएंगे।
class App : Application() {
override fun onCreate() {
super.onCreate()
}
}
माई क्रिप्टो कॉइन्स ऐप के लिए, हमने पहले ही एप्लिकेशन क्लास बना ली है।
अगला, अपना 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
.
यहां हम डैगर कार्यान्वयन से संबंधित सभी फाइलों को रखने जा रहे हैं।
बनाएं AppModule
क्लास मॉड्यूल जो आपके पूरे आवेदन पर निर्भरता प्रदान करेगा।
/**
* AppModule will provide app-wide dependencies for a part of the application.
* It should initialize objects used across our application, such as Room database, Retrofit, Shared Preference, etc.
*/
@Module(includes = [ViewModelsModule::class])
class AppModule() {
@Singleton // Annotation informs Dagger compiler that the instance should be created only once in the entire lifecycle of the application.
@Provides // Annotation informs Dagger compiler that this method is the constructor for the Context return type.
fun provideContext(app: App): Context = app // Using provide as a prefix is a common convention but not a requirement.
@Singleton
@Provides
fun provideCryptocurrencyRepository(context: Context): CryptocurrencyRepository {
return CryptocurrencyRepository.getInstance(AppDatabase.getInstance(context).cryptocurrencyDao())
}
}
जैसा कि आप देख सकते हैं, एक डैगर मॉड्यूल बनाने के लिए हमें इसे विशेष @Module
के साथ एनोटेट करना होगा एनोटेशन। परियोजनाओं में आमतौर पर कई डैगर मॉड्यूल होते हैं। उनमें से एक के लिए ऐप-वाइड निर्भरता प्रदान करना विशिष्ट है। यह AppModule
हमारे एप्लिकेशन में उपयोग की जाने वाली वस्तुओं को प्रारंभ करने के लिए उपयोग किया जाएगा, जैसे रूम डेटाबेस, रेट्रोफिट, साझा वरीयता, आदि।
एक उदाहरण के रूप में, हम ऐपमॉड्यूल के लिए एक बहुत ही सामान्य परिदृश्य पर चर्चा कर सकते हैं ताकि अगर हमें अपने ऐप में कहीं भी इसे एक्सेस करने की आवश्यकता हो तो एक संदर्भ वस्तु प्रदान करें। यह कैसे करना है, यह देखने के लिए आइए कोड का विश्लेषण करें।
हमें एक विशेष डैगर एनोटेशन का उपयोग करने की आवश्यकता है @Provides
. यह डैगर को बताता है कि विधि एक विशिष्ट प्रकार की निर्भरता प्रदान करती है, हमारे मामले में, एक संदर्भ वस्तु। तो जब कहीं ऐप में हम एक संदर्भ को इंजेक्ट करने का अनुरोध करते हैं, तो AppModule वह स्थान है जहां डैगर इसे ढूंढता है। और यह हमारे तरीकों के नाम से कोई फर्क नहीं पड़ता, क्योंकि डैगर केवल रिटर्न प्रकार की परवाह करता है। उपसर्ग प्रदान करने वाली विधि को नाम देना केवल सामान्य बात है, लेकिन यह कुछ भी हो सकता है जो आप चाहते हैं।
@Singleton
एनोटेशन जिसे आप उसी विधि पर लागू होते हुए देखते हैं, डैगर एनोटेशन का हिस्सा नहीं है। यह javax पैकेज के अंदर समाहित है। यह एनोटेशन डैगर को बताता है कि उस निर्भरता का केवल एक ही उदाहरण होना चाहिए।
ऑब्जेक्ट का एक और उदाहरण पहले से उपलब्ध है या नहीं, यह जांचने के लिए आपको बॉयलरप्लेट कोड लिखने की आवश्यकता नहीं है। कोड जनरेट करते समय डैगर इस एनोटेशन के कारण आपके लिए उस सारे तर्क को संभाल लेगा। ध्यान दें कि हमारे AppModule में एक और मॉड्यूल ViewModelsModule शामिल है। चलिए इसे अभी बनाते हैं।
एक ViewModelsModule
बनाएं कक्षा मॉड्यूल। यह मॉड्यूल आपके पूरे आवेदन में ViewModels प्रदान करने के लिए जिम्मेदार होगा।
/**
* Will be responsible for providing ViewModels.
*/
@Module
abstract class ViewModelsModule {
// We'd like to take this implementation of the ViewModel class and make it available in an injectable map with MainViewModel::class as a key to that map.
@Binds
@IntoMap
@ViewModelKey(MainViewModel::class) // We use a restriction on multibound map defined with @ViewModelKey annotation, and if don't need any, we should use @ClassKey annotation provided by Dagger.
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
}
यह मॉड्यूल डैगर 2 फीचर मैप मल्टी बाइंडिंग का उपयोग करता है। इसका उपयोग करके, हम अपने चयन की वस्तुओं को एक मानचित्र में योगदान करते हैं जो हमारे ऐप में कहीं भी इंजेक्शन योग्य हो जाता है। डैगर एनोटेशन के संयोजन का उपयोग करना @Binds
, @IntoMap
और हमारा कस्टम एनोटेशन @ViewModelKey
(इसे हम बनाने जा रहे हैं), हम अपने मानचित्र के अंदर कुंजी MainViewModel::class
. के साथ एक प्रविष्टि बनाते हैं और मान MainViewModel
उदाहरण। हम कुछ सामान्य ViewModelFactory
. की सहायता से विशिष्ट फ़ैक्टरी को बाँधते हैं कक्षा। हमें इस वर्ग को बनाने की जरूरत है।
कस्टम एनोटेशन क्लास बनाएं ViewModelKey
.
/**
* An annotation class which tells dagger that it can be used to determine keys in multi bound maps.
*/
@MustBeDocumented
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>) // We might use only those classes which inherit from ViewModel.
इस वर्ग का उपयोग ViewModels को ViewModelsModule
. में बाँधने के लिए किया जाता है . विशिष्ट एनोटेशन @ViewModelKey
हमारे नक्शे की कुंजी का प्रतिनिधित्व करता है। हमारी कुंजी केवल एक वर्ग हो सकती है जो ViewModel
. से इनहेरिट करती है ।
ViewModelFactory
बनाएं कक्षा।
/**
* Factory to auto-generate a Class to Provider Map.
* We use Provider<T> to create an injectable object at a later time.
*/
@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
बनाएं कक्षा मॉड्यूल।
/**
* All activities intended to use Dagger @Inject should be listed here.
*/
@Module
abstract class ActivityBuildersModule {
@ContributesAndroidInjector(modules = [MainListFragmetBuildersModule::class]) // Where to apply the injection.
abstract fun contributeMainActivity(): MainActivity
@ContributesAndroidInjector
abstract fun contributeAddSearchActivity(): AddSearchActivity
}
यह मॉड्यूल आपकी सभी गतिविधियों के निर्माण के लिए जिम्मेदार है। यह AndroidInjector
उत्पन्न करेगा इस कक्षा में परिभाषित सभी गतिविधियों के लिए। फिर वस्तुओं को AndroidInjection.inject(this)
. का उपयोग करके गतिविधियों में अंतःक्षिप्त किया जा सकता है onCreate
. में गतिविधि जीवनचक्र से कार्य। ध्यान दें कि यह मॉड्यूल टुकड़ों के लिए जिम्मेदार एक अन्य अलग मॉड्यूल का भी उपयोग करता है। हम आगे इस मॉड्यूल को बनाएंगे।
MainListFragmetBuildersModule
बनाएं कक्षा मॉड्यूल।
/**
* All fragments related to MainActivity intended to use Dagger @Inject should be listed here.
*/
@Module
abstract class MainListFragmetBuildersModule {
@ContributesAndroidInjector() // Attaches fragment to Dagger graph.
abstract fun contributeMainListFragment(): MainListFragment
}
यह मॉड्यूल MainActivity
. से संबंधित आपके सभी अंशों का निर्माण करेगा . यह AndroidInjector
generate उत्पन्न करेगा इस वर्ग में परिभाषित सभी टुकड़ों के लिए। वस्तुओं को AndroidSupportInjection.inject(this)
. का उपयोग करके टुकड़ों में अंतःक्षिप्त किया जा सकता है onAttach
. में खंड जीवनचक्र से कार्य करता है।
AppComponent
बनाएं वर्ग घटक।
/**
* Singleton component interface for the app. It ties all the modules together.
* The component is used to connect objects to their dependencies.
* Dagger will auto-generate DaggerAppComponent which is used for initialization at Application.
*/
@Singleton
@Component(
modules = [
// AndroidSupportInjectionModule is a class of Dagger and we don't need to create it.
// If you want to use injection in fragment then you should use AndroidSupportInjectionModule.class else use AndroidInjectionModule.
AndroidSupportInjectionModule::class,
AppModule::class,
ActivityBuildersModule::class
]
)
interface AppComponent {
@Component.Builder // Used for instantiation of a component.
interface Builder {
@BindsInstance // Bind our application instance to our Dagger graph.
fun application(application: App): Builder
fun build(): AppComponent
}
// The application which is allowed to request the dependencies declared by the modules
// (by means of the @Inject annotation) should be declared here with individual inject() methods.
fun inject(app: App)
}
घटक एक बहुत ही महत्वपूर्ण वर्ग है। यह उपरोक्त सभी को एक साथ काम करना शुरू करने में सक्षम करेगा। यह वस्तुओं को उनकी निर्भरता से जोड़कर करता है। निर्भरता इंजेक्शन करने के लिए आवश्यक कोड उत्पन्न करने के लिए डैगर इस इंटरफ़ेस का उपयोग करेगा।
एक घटक वर्ग बनाने के लिए आपको डैगर एनोटेशन @Component
. का उपयोग करना होगा . यह एक इनपुट के रूप में मॉड्यूल की एक सूची लेता है। एक और एनोटेशन @Component.Builder
हमें कुछ उदाहरणों को घटक से बाँधने की अनुमति देता है।
फिर एक ग्राफ़ ऑब्जेक्ट जेनरेट करें।
इस समय आपके पास अपने सभी मॉड्यूल और आपके घटक सेटअप हैं। आप अपने एंड्रॉइड स्टूडियो आईडीई के अंदर बिल्ड -> मॉड्यूल बनाएं का चयन करके अपना ग्राफ ऑब्जेक्ट जेनरेट कर सकते हैं। भविष्य के चरणों के लिए हमें इस पीढ़ी की आवश्यकता होगी।
अब एक Injectable
बनाएं इंटरफ़ेस।
/**
* It is just a plain empty marker interface, which tells to automatically inject activities or fragments if they implement it.
*/
interface Injectable
भविष्य के कदमों के लिए भी इसकी आवश्यकता होगी। Injectable
इंटरफ़ेस को उन गतिविधियों या अंशों द्वारा कार्यान्वित किया जाना चाहिए जिन्हें हम स्वचालित रूप से इंजेक्शन योग्य बनाना चाहते हैं।
AppInjector
. नामक एक नया सहायक वर्ग बनाएं .
/**
* It is simple helper class to avoid calling inject method on each activity or fragment.
*/
object AppInjector {
fun init(app: App) {
// Here we initialize Dagger. DaggerAppComponent is auto-generated from 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) {
// Calling inject() method will cause Dagger to locate the singletons in the dependency graph to try to find a matching return type.
// If it finds one, it assigns the references to the respective fields.
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)
}
}
}
प्रत्येक गतिविधि या खंड पर इंजेक्शन विधि को कॉल करने से बचने के लिए यह सिर्फ एक साधारण सहायक वर्ग है।
अगला, App
सेटअप करें क्लास जिसे हम पहले ही बना चुके हैं।
class App : Application(), HasActivityInjector {
@Inject // It implements Dagger machinery of finding appropriate injector factory for a type.
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
// Initialize in order to automatically inject activities and fragments if they implement Injectable interface.
AppInjector.init(this)
...
}
// This is required by HasActivityInjector interface to setup Dagger for Activity.
override fun activityInjector(): AndroidInjector<Activity> = dispatchingAndroidInjector
}
चूंकि एप्लिकेशन में गतिविधियां हैं, इसलिए हमें HasActivityInjector
. को लागू करने की आवश्यकता है इंटरफेस। अगर आपको DaggerAppComponent
. पर Android Studio द्वारा कॉल की गई कोई त्रुटि दिखाई देती है , ऐसा इसलिए है क्योंकि आपने एक नई फ़ाइल नहीं बनाई है, जैसा कि पिछले चरण में बताया गया था।
तो, सेटअप करें MainActivity
मुख्य ViewModel फ़ैक्टरी को इंजेक्ट करने और फ़्रैगमेंट इंजेक्शन के लिए समर्थन जोड़ने के लिए।
// To support injecting fragments which belongs to this activity we need to implement HasSupportFragmentInjector.
// We would not need to implement it, if our activity did not contain any fragments or the fragments did not need to inject anything.
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)
// Obtain ViewModel from ViewModelProviders, using this activity as LifecycleOwner.
mainViewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)
...
}
...
override fun supportFragmentInjector(): AndroidInjector<Fragment> = dispatchingAndroidInjector
...
}
चूंकि हमारी गतिविधियों में बच्चे के टुकड़े हैं, इसलिए हमें HasSupportFragmentInjector
. को लागू करने की आवश्यकता है इंटरफेस। हमें इसकी भी आवश्यकता है क्योंकि हम अपने टुकड़ों में इंजेक्शन बनाने की योजना बना रहे हैं। हमारी गतिविधि को यह नहीं पता होना चाहिए कि इसे कैसे इंजेक्ट किया जाता है। हम AndroidInjection.inject(this)
. का उपयोग करते हैं ओवरराइडिंग के अंदर कोड लाइन onCreate()
विधि।
कॉलिंग inject()
विधि डैगर 2 को एक मिलान रिटर्न प्रकार खोजने का प्रयास करने के लिए निर्भरता ग्राफ में सिंगलटन का पता लगाने का कारण बनेगी। हालाँकि हमें यहाँ कोई कोड लिखने की आवश्यकता नहीं है क्योंकि यह हमारे लिए पहले बनाए गए AppInjector
द्वारा किया गया है हेल्पर क्लास जिसे हमने अपने एप्लीकेशन क्लास के अंदर इनिशियलाइज़ किया था।
फिर, सेटअप करें MainListFragment
मुख्य ViewModel फ़ैक्टरी को इंजेक्ट करने के लिए।
/**
* A placeholder fragment containing a simple view.
*/
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) {
// Obtain ViewModel from ViewModelProviders, using parent activity as LifecycleOwner.
viewModel = ViewModelProviders.of(activity, viewModelFactory).get(MainViewModel::class.java)
...
}
}
गतिविधियों के समान, यदि हम चाहते हैं कि हमारा टुकड़ा इंजेक्शन योग्य हो, तो उसके onAttach
. में विधि हमें कोड लिखना चाहिए AndroidSupportInjection.inject(this)
. लेकिन फिर से यह AppInjector
. द्वारा किया गया काम है सहायक, तो हम उसे छोड़ सकते हैं। बस ध्यान दें कि हमें Injectable
जोड़ना होगा इंटरफ़ेस जिसे हमने पहले हेल्पर के काम करने के लिए बनाया था।
बधाई हो, हमने माई क्रिप्टो कॉइन्स ऐप प्रोजेक्ट में डैगर 2 को लागू किया है। बेशक यह लेख आपके ऐप में सीधे डैगर 2 को तैनात करने के लिए एक त्वरित मार्गदर्शिका है, लेकिन इसकी गहरी कवरेज नहीं है। मेरा सुझाव है कि यदि आप बुनियादी बातों में खोया हुआ महसूस करते हैं तो आप इस विषय पर शोध करना जारी रखें।
रिपॉजिटरी
GitHub पर अपडेट किए गए "क्रिप्टोफोलियो" (पहले "माई क्रिप्टो कॉइन्स") ऐप का सोर्स कोड देखें।
GitHub पर स्रोत देखें
आशी! पढ़ने के लिए धन्यवाद! मैंने मूल रूप से इस पोस्ट को अपने निजी ब्लॉग www.baruckis.com के लिए 7 अक्टूबर 2018 को प्रकाशित किया था।