Hilt, Android के लिए डिपेंडेंसी इंजेक्शन लाइब्रेरी है. इससे आपके प्रोजेक्ट में मैन्युअल डिपेंडेंसी इंजेक्शन करने के लिए, बोइलरप्लेट की ज़रूरत कम हो जाती है. मैन्युअल डिपेंडेंसी इंजेक्शन करने के लिए, आपको हर क्लास और उसकी डिपेंडेंसी को मैन्युअल तरीके से बनाना होगा. साथ ही, डिपेंडेंसी का फिर से इस्तेमाल करने और उन्हें मैनेज करने के लिए, कंटेनर का इस्तेमाल करना होगा.
Hilt, आपके ऐप्लिकेशन में डीआई का इस्तेमाल करने का एक स्टैंडर्ड तरीका उपलब्ध कराता है. इसके लिए, यह आपके प्रोजेक्ट में मौजूद हर Android क्लास के लिए कंटेनर उपलब्ध कराता है और उनके लाइफ़साइकल को अपने-आप मैनेज करता है. Hilt को लोकप्रिय डीआई लाइब्रेरी Dagger के ऊपर बनाया गया है. इससे, कंपाइल के समय सही होने, रनटाइम परफ़ॉर्मेंस, स्केलेबिलिटी, और Android Studio के साथ काम करने की सुविधा का फ़ायदा मिलता है. ज़्यादा जानकारी के लिए, हिल्ट और डैगर देखें.
इस गाइड में, Hilt और इसके जनरेट किए गए कंटेनर के बुनियादी कॉन्सेप्ट के बारे में बताया गया है. इसमें, Hilt का इस्तेमाल करने के लिए किसी मौजूदा ऐप्लिकेशन को बूटस्ट्रैप करने का तरीका भी बताया गया है.
डिपेंडेंसी जोड़ना
सबसे पहले, अपने प्रोजेक्ट की रूट
build.gradle
फ़ाइल में hilt-android-gradle-plugin
प्लग इन जोड़ें:
Groovy
plugins { ... id 'com.google.dagger.hilt.android' version '2.51.1' apply false }
Kotlin
plugins { ... id("com.google.dagger.hilt.android") version "2.51.1" apply false }
इसके बाद, Gradle प्लग इन लागू करें और अपनी app/build.gradle
फ़ाइल में ये डिपेंडेंसी जोड़ें:
Groovy
... plugins { id 'kotlin-kapt' id 'com.google.dagger.hilt.android' } android { ... } dependencies { implementation "com.google.dagger:hilt-android:2.51.1" kapt "com.google.dagger:hilt-compiler:2.51.1" } // Allow references to generated code kapt { correctErrorTypes true }
Kotlin
plugins { id("kotlin-kapt") id("com.google.dagger.hilt.android") } android { ... } dependencies { implementation("com.google.dagger:hilt-android:2.51.1") kapt("com.google.dagger:hilt-android-compiler:2.51.1") } // Allow references to generated code kapt { correctErrorTypes = true }
Hilt, Java 8 की सुविधाओं का इस्तेमाल करता है. अपने प्रोजेक्ट में Java 8 को चालू करने के लिए, app/build.gradle
फ़ाइल में ये चीज़ें जोड़ें:
Groovy
android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } }
Kotlin
android { ... compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } }
Hilt ऐप्लिकेशन क्लास
Hilt का इस्तेमाल करने वाले सभी ऐप्लिकेशन में, Application
क्लास होनी चाहिए. साथ ही, उस पर @HiltAndroidApp
एनोटेशन होना चाहिए.
@HiltAndroidApp
, Hilt के कोड जनरेशन को ट्रिगर करता है. इसमें आपके ऐप्लिकेशन के लिए एक बेस क्लास भी शामिल होती है, जो ऐप्लिकेशन-लेवल के डिपेंडेंसी कंटेनर के तौर पर काम करती है.
Kotlin
@HiltAndroidApp class ExampleApplication : Application() { ... }
Java
@HiltAndroidApp public class ExampleApplication extends Application { ... }
जनरेट किया गया यह Hilt कॉम्पोनेंट, Application
ऑब्जेक्ट के लाइफ़साइकल से जुड़ा होता है और उसे डिपेंडेंसी देता है. इसके अलावा, यह ऐप्लिकेशन का पैरंट कॉम्पोनेंट होता है. इसका मतलब है कि अन्य कॉम्पोनेंट, इसकी डिपेंडेंसी ऐक्सेस कर सकते हैं.
Android क्लास में डिपेंडेंसी इंजेक्ट करना
जब Hilt को आपकी Application
क्लास में सेट अप कर दिया जाता है और ऐप्लिकेशन-लेवल का कॉम्पोनेंट उपलब्ध हो जाता है, तो Hilt उन अन्य Android क्लास के लिए डिपेंडेंसी उपलब्ध करा सकता है जिनमें @AndroidEntryPoint
एनोटेशन है:
Kotlin
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { ... }
Java
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { ... }
फ़िलहाल, Hilt इन Android क्लास के साथ काम करता है:
Application
(@HiltAndroidApp
का इस्तेमाल करके)ViewModel
(@HiltViewModel
का इस्तेमाल करके)Activity
Fragment
View
Service
BroadcastReceiver
अगर किसी Android क्लास को @AndroidEntryPoint
के साथ एनोटेट किया जाता है, तो आपको उस पर निर्भर Android क्लास को भी एनोटेट करना होगा. उदाहरण के लिए, अगर आपने किसी फ़्रैगमेंट पर एनोटेट किया है, तो आपको उन सभी गतिविधियों पर भी एनोटेट करना होगा जिनमें उस फ़्रैगमेंट का इस्तेमाल किया गया है.
@AndroidEntryPoint
आपके प्रोजेक्ट में मौजूद हर Android क्लास के लिए, एक अलग Hilt कॉम्पोनेंट जनरेट करता है. इन कॉम्पोनेंट को, कॉम्पोनेंट के क्रम में बताए गए तरीके के मुताबिक, अपनी पैरंट क्लास से डिपेंडेंसी मिल सकती हैं.
किसी कॉम्पोनेंट से डिपेंडेंसी पाने के लिए, फ़ील्ड इंजेक्शन करने के लिए @Inject
एनोटेशन का इस्तेमाल करें:
Kotlin
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { @Inject lateinit var analytics: AnalyticsAdapter ... }
Java
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { @Inject AnalyticsAdapter analytics; ... }
Hilt जिन क्लास में इंजेक्शन करता है उनमें ऐसी अन्य बेस क्लास भी हो सकती हैं जो इंजेक्शन का इस्तेमाल करती हैं.
अगर क्लास एब्स्ट्रैक्ट हैं, तो उन्हें @AndroidEntryPoint
एनोटेशन की ज़रूरत नहीं होती.
किसी Android क्लास में किस लाइफ़साइकल कॉलबैक को इंजेक्ट किया जाता है, इस बारे में ज़्यादा जानने के लिए, कॉम्पोनेंट के लाइफ़टाइम देखें.
Hilt बाइंडिंग तय करना
फ़ील्ड इंजेक्शन करने के लिए, Hilt को यह जानना होगा कि उससे जुड़े कॉम्पोनेंट से ज़रूरी डिपेंडेंसी के इंस्टेंस कैसे उपलब्ध कराए जाएं. बाइंडिंग में, किसी टाइप के इंस्टेंस को डिपेंडेंसी के तौर पर उपलब्ध कराने के लिए ज़रूरी जानकारी होती है.
Hilt को बाइंडिंग की जानकारी देने का एक तरीका, कंस्ट्रक्टर इंजेक्शन है. किसी क्लास के कंस्ट्रक्टर पर @Inject
एनोटेशन का इस्तेमाल करके, Hilt को उस क्लास के इंस्टेंस उपलब्ध कराने का तरीका बताएं:
Kotlin
class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
Java
public class AnalyticsAdapter { private final AnalyticsService service; @Inject AnalyticsAdapter(AnalyticsService service) { this.service = service; } ... }
किसी क्लास के एनोटेट किए गए कंस्ट्रक्टर के पैरामीटर, उस क्लास की डिपेंडेंसी होते हैं. उदाहरण में, AnalyticsAdapter
के लिए AnalyticsService
डिपेंडेंसी है. इसलिए, Hilt को AnalyticsService
के इंस्टेंस उपलब्ध कराने का तरीका भी पता होना चाहिए.
Hilt मॉड्यूल
कभी-कभी किसी टाइप को कन्स्ट्रक्टर इंजेक्शन नहीं किया जा सकता. ऐसा कई वजहों से हो सकता है. उदाहरण के लिए, किसी इंटरफ़ेस को कन्स्ट्रक्टर-इंजेक्ट नहीं किया जा सकता. आपके पास ऐसे टाइप को कंस्ट्रक्टर इंजेक्ट करने का विकल्प भी नहीं है जिसका मालिकाना हक आपके पास नहीं है. जैसे, किसी बाहरी लाइब्रेरी की क्लास. ऐसे मामलों में, Hilt मॉड्यूल का इस्तेमाल करके, Hilt को बाइंडिंग की जानकारी दी जा सकती है.
Hilt मॉड्यूल एक ऐसी क्लास होती है जिसे @Module
के साथ एनोटेट किया जाता है. Dagger
module की तरह, यह Hilt को कुछ खास तरह के इंस्टेंस उपलब्ध कराने का तरीका बताता है. Dagger मॉड्यूल के उलट, आपको Hilt मॉड्यूल को @InstallIn
के साथ एनोटेट करना होगा. इससे Hilt को यह पता चलता है कि हर मॉड्यूल का इस्तेमाल या इंस्टॉल किस Android क्लास में किया जाएगा.
Hilt मॉड्यूल में दी गई डिपेंडेंसी, जनरेट किए गए उन सभी कॉम्पोनेंट में उपलब्ध होती हैं जो उस Android क्लास से जुड़े होते हैं जहां Hilt मॉड्यूल इंस्टॉल किया जाता है.
@Binds की मदद से इंटरफ़ेस इंस्टेंस इंजेक्ट करना
AnalyticsService
उदाहरण देखें. अगर AnalyticsService
कोई इंटरफ़ेस है, तो उसे कंस्ट्रक्टर-इंजेक्ट नहीं किया जा सकता. इसके बजाय, Hilt को बाइंडिंग की जानकारी दें. इसके लिए, Hilt मॉड्यूल में @Binds
के साथ एनोटेट किया गया एक एब्स्ट्रैक्ट फ़ंक्शन बनाएं.
@Binds
एनोटेशन से Hilt को पता चलता है कि किसी इंटरफ़ेस का इंस्टेंस उपलब्ध कराने के लिए, उसे किस तरीके का इस्तेमाल करना है.
एनोटेट किया गया फ़ंक्शन, Hilt को यह जानकारी देता है:
- फ़ंक्शन के रिटर्न टाइप से Hilt को पता चलता है कि फ़ंक्शन किस इंटरफ़ेस के इंस्टेंस उपलब्ध कराता है.
- फ़ंक्शन पैरामीटर से Hilt को यह पता चलता है कि कौनसा लागू करना है.
Kotlin
interface AnalyticsService { fun analyticsMethods() } // Constructor-injected, because Hilt needs to know how to // provide instances of AnalyticsServiceImpl, too. class AnalyticsServiceImpl @Inject constructor( ... ) : AnalyticsService { ... } @Module @InstallIn(ActivityComponent::class) abstract class AnalyticsModule { @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService }
Java
public interface AnalyticsService { void analyticsMethods(); } // Constructor-injected, because Hilt needs to know how to // provide instances of AnalyticsServiceImpl, too. public class AnalyticsServiceImpl implements AnalyticsService { ... @Inject AnalyticsServiceImpl(...) { ... } } @Module @InstallIn(ActivityComponent.class) public abstract class AnalyticsModule { @Binds public abstract AnalyticsService bindAnalyticsService( AnalyticsServiceImpl analyticsServiceImpl ); }
Hilt मॉड्यूल AnalyticsModule
को @InstallIn(ActivityComponent.class)
के साथ एनोटेट किया गया है, क्योंकि आपको Hilt को ExampleActivity
में उस डिपेंडेंसी को इंजेक्ट करना है. इस एनोटेशन का मतलब है कि AnalyticsModule
में मौजूद सभी डिपेंडेंसी, ऐप्लिकेशन की सभी गतिविधियों में उपलब्ध हैं.
@Provides का इस्तेमाल करके इंस्टेंस इंजेक्ट करना
इंटरफ़ेस ही एकमात्र ऐसा मामला नहीं है जहां किसी टाइप को कन्स्ट्रक्टर-इंजेक्ट नहीं किया जा सकता.
अगर आपके पास क्लास का मालिकाना हक नहीं है, तो कंस्ट्रक्टर इंजेक्शन भी नहीं किया जा सकता. ऐसा इसलिए होता है, क्योंकि यह किसी बाहरी लाइब्रेरी (जैसे, Retrofit, OkHttpClient
या Room डेटाबेस जैसी क्लास) से मिलता है. इसके अलावा, अगर बिल्डर पैटर्न का इस्तेमाल करके इंस्टेंस बनाने ज़रूरी हैं, तो भी कंस्ट्रक्टर इंजेक्शन नहीं किया जा सकता.
पिछले उदाहरण पर ध्यान दें. अगर आपके पास सीधे तौर पर AnalyticsService
क्लास का मालिकाना हक नहीं है, तो Hilt को इस टाइप के इंस्टेंस उपलब्ध कराने का तरीका बताया जा सकता है. इसके लिए, Hilt मॉड्यूल में कोई फ़ंक्शन बनाएं और उस फ़ंक्शन को @Provides
के साथ एनोटेट करें.
एनोटेट किया गया फ़ंक्शन, Hilt को यह जानकारी देता है:
- फ़ंक्शन के रिटर्न टाइप से Hilt को पता चलता है कि फ़ंक्शन किस तरह के इंस्टेंस उपलब्ध कराता है.
- फ़ंक्शन पैरामीटर, Hilt को उस टाइप की डिपेंडेंसी के बारे में बताते हैं.
- फ़ंक्शन का मुख्य हिस्सा, Hilt को उस टाइप का इंस्टेंस उपलब्ध कराने का तरीका बताता है. जब भी Hilt को किसी टाइप का इंस्टेंस देना होता है, तो वह फ़ंक्शन के मुख्य हिस्से को लागू करता है.
Kotlin
@Module @InstallIn(ActivityComponent::class) object AnalyticsModule { @Provides fun provideAnalyticsService( // Potential dependencies of this type ): AnalyticsService { return Retrofit.Builder() .baseUrl("https://example.com") .build() .create(AnalyticsService::class.java) } }
Java
@Module @InstallIn(ActivityComponent.class) public class AnalyticsModule { @Provides public static AnalyticsService provideAnalyticsService( // Potential dependencies of this type ) { return new Retrofit.Builder() .baseUrl("https://example.com") .build() .create(AnalyticsService.class); } }
एक ही टाइप के लिए कई बाइंडिंग उपलब्ध कराना
अगर आपको Hilt को डिपेंडेंसी के तौर पर एक ही तरह के अलग-अलग लागू करने की ज़रूरत है, तो आपको Hilt को कई बाइंडिंग देनी होंगी. क्वालिफ़ायर की मदद से, एक ही टाइप के लिए कई बाइंडिंग तय की जा सकती हैं.
क्वालिफ़ायर एक ऐसा एनोटेशन है जिसका इस्तेमाल किसी टाइप के लिए किसी खास बाइंडिंग की पहचान करने के लिए किया जाता है. ऐसा तब किया जाता है, जब उस टाइप के लिए एक से ज़्यादा बाइंडिंग तय की गई हों.
उदाहरण देखें. अगर आपको AnalyticsService
पर आने वाले कॉल को इंटरसेप्ट करना है, तो इंटरसेप्टर के साथ OkHttpClient
ऑब्जेक्ट का इस्तेमाल किया जा सकता है. अन्य सेवाओं के लिए, आपको कॉल को किसी दूसरे तरीके से इंटरसेप्ट करना पड़ सकता है. ऐसे में, आपको Hilt को यह बताना होगा कि OkHttpClient
के दो अलग-अलग इंप्लीमेंटेशन कैसे उपलब्ध कराएं.
सबसे पहले, वे क्वालिफ़ायर तय करें जिनका इस्तेमाल करके, @Binds
या
@Provides
तरीकों को एनोटेट किया जाएगा:
Kotlin
@Qualifier @Retention(AnnotationRetention.BINARY) annotation class AuthInterceptorOkHttpClient @Qualifier @Retention(AnnotationRetention.BINARY) annotation class OtherInterceptorOkHttpClient
Java
@Qualifier @Retention(RetentionPolicy.RUNTIME) private @interface AuthInterceptorOkHttpClient {} @Qualifier @Retention(RetentionPolicy.RUNTIME) private @interface OtherInterceptorOkHttpClient {}
इसके बाद, Hilt को यह पता होना चाहिए कि हर क्वालीफ़ायर के हिसाब से, टाइप का इंस्टेंस कैसे दिया जाए. इस मामले में, @Provides
के साथ Hilt मॉड्यूल का इस्तेमाल किया जा सकता है.
दोनों तरीकों में रिटर्न टाइप एक जैसा होता है, लेकिन क्वालिफ़ायर उन्हें दो अलग-अलग बाइंडिंग के तौर पर लेबल करते हैं:
Kotlin
@Module @InstallIn(SingletonComponent::class) object NetworkModule { @AuthInterceptorOkHttpClient @Provides fun provideAuthInterceptorOkHttpClient( authInterceptor: AuthInterceptor ): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(authInterceptor) .build() } @OtherInterceptorOkHttpClient @Provides fun provideOtherInterceptorOkHttpClient( otherInterceptor: OtherInterceptor ): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(otherInterceptor) .build() } }
Java
@Module @InstallIn(ActivityComponent.class) public class NetworkModule { @AuthInterceptorOkHttpClient @Provides public static OkHttpClient provideAuthInterceptorOkHttpClient( AuthInterceptor authInterceptor ) { return new OkHttpClient.Builder() .addInterceptor(authInterceptor) .build(); } @OtherInterceptorOkHttpClient @Provides public static OkHttpClient provideOtherInterceptorOkHttpClient( OtherInterceptor otherInterceptor ) { return new OkHttpClient.Builder() .addInterceptor(otherInterceptor) .build(); } }
फ़ील्ड या पैरामीटर को उससे जुड़ी क्वालीफ़ायर के साथ एनोटेट करके, अपनी ज़रूरत के हिसाब से टाइप इंजेक्ट किया जा सकता है:
Kotlin
// As a dependency of another class. @Module @InstallIn(ActivityComponent::class) object AnalyticsModule { @Provides fun provideAnalyticsService( @AuthInterceptorOkHttpClient okHttpClient: OkHttpClient ): AnalyticsService { return Retrofit.Builder() .baseUrl("https://example.com") .client(okHttpClient) .build() .create(AnalyticsService::class.java) } } // As a dependency of a constructor-injected class. class ExampleServiceImpl @Inject constructor( @AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient ) : ... // At field injection. @AndroidEntryPoint class ExampleActivity: AppCompatActivity() { @AuthInterceptorOkHttpClient @Inject lateinit var okHttpClient: OkHttpClient }
Java
// As a dependency of another class. @Module @InstallIn(ActivityComponent.class) public class AnalyticsModule { @Provides public static AnalyticsService provideAnalyticsService( @AuthInterceptorOkHttpClient OkHttpClient okHttpClient ) { return new Retrofit.Builder() .baseUrl("https://example.com") .client(okHttpClient) .build() .create(AnalyticsService.class); } } // As a dependency of a constructor-injected class. public class ExampleServiceImpl ... { private final OkHttpClient okHttpClient; @Inject ExampleServiceImpl(@AuthInterceptorOkHttpClient OkHttpClient okHttpClient) { this.okHttpClient = okHttpClient; } } // At field injection. @AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { @AuthInterceptorOkHttpClient @Inject OkHttpClient okHttpClient; ... }
सबसे सही तरीका यह है कि अगर किसी टाइप में कोई क्वालिफ़ायर जोड़ा जाता है, तो उस डिपेंडेंसी को उपलब्ध कराने के सभी संभावित तरीकों में क्वालिफ़ायर जोड़ें. बुनियादी या सामान्य लागू करने के तरीके को बिना क्वालिफ़ायर के छोड़ने पर गड़बड़ियां हो सकती हैं. साथ ही, इसकी वजह से Hilt गलत डिपेंडेंसी इंजेक्ट कर सकता है.
Hilt में पहले से तय किए गए क्वालिफ़ायर
Hilt में पहले से तय कुछ क्वालिफ़ायर मौजूद होते हैं. उदाहरण के लिए, हो सकता है कि आपको ऐप्लिकेशन या गतिविधि में से किसी एक से Context
क्लास की ज़रूरत पड़े. इसलिए, Hilt @ApplicationContext
और @ActivityContext
क्वालिफ़ायर उपलब्ध कराता है.
मान लें कि उदाहरण में दी गई AnalyticsAdapter
क्लास को गतिविधि के संदर्भ की ज़रूरत है. यहां दिए गए कोड में, AnalyticsAdapter
को गतिविधि का कॉन्टेक्स्ट देने का तरीका बताया गया है:
Kotlin
class AnalyticsAdapter @Inject constructor( @ActivityContext private val context: Context, private val service: AnalyticsService ) { ... }
Java
public class AnalyticsAdapter { private final Context context; private final AnalyticsService service; @Inject AnalyticsAdapter( @ActivityContext Context context, AnalyticsService service ) { this.context = context; this.service = service; } }
Hilt में पहले से तय अन्य बाइंडिंग के लिए, कॉम्पोनेंट की डिफ़ॉल्ट बाइंडिंग देखें.
Android क्लास के लिए जनरेट किए गए कॉम्पोनेंट
हर उस Android क्लास के लिए, जिसमें फ़ील्ड इंजेक्शन किया जा सकता है, उससे जुड़ा एक Hilt कॉम्पोनेंट होता है. इसका रेफ़रंस @InstallIn
एनोटेशन में दिया जा सकता है.
हर Hilt कॉम्पोनेंट, अपनी बाइंडिंग को उससे जुड़ी Android क्लास में इंजेक्ट करने के लिए ज़िम्मेदार होता है.
पिछले उदाहरणों में, Hilt के मॉड्यूल में ActivityComponent
के इस्तेमाल के बारे में बताया गया है.
Hilt में ये कॉम्पोनेंट होते हैं:
Hilt कॉम्पोनेंट | Injector for |
---|---|
SingletonComponent |
Application |
ActivityRetainedComponent |
लागू नहीं |
ViewModelComponent |
ViewModel |
ActivityComponent |
Activity |
FragmentComponent |
Fragment |
ViewComponent |
View |
ViewWithFragmentComponent |
View पर @WithFragmentBindings वाला एनोटेशन |
ServiceComponent |
Service |
कॉम्पोनेंट के लाइफ़टाइम
Hilt, जनरेट की गई कॉम्पोनेंट क्लास के इंस्टेंस को अपने-आप बनाता और नष्ट करता है. ऐसा, उनसे जुड़ी Android क्लास के लाइफ़साइकल के हिसाब से किया जाता है.
जनरेट किया गया कॉम्पोनेंट | मैसेज किस समय लिखा गया | नष्ट करने की तारीख |
---|---|---|
SingletonComponent |
Application#onCreate() |
Application मिटा दिया गया |
ActivityRetainedComponent |
Activity#onCreate() |
Activity#onDestroy() |
ViewModelComponent |
ViewModel बनाया गया |
ViewModel मिटा दिया गया |
ActivityComponent |
Activity#onCreate() |
Activity#onDestroy() |
FragmentComponent |
Fragment#onAttach() |
Fragment#onDestroy() |
ViewComponent |
View#super() |
View मिटा दिया गया |
ViewWithFragmentComponent |
View#super() |
View मिटा दिया गया |
ServiceComponent |
Service#onCreate() |
Service#onDestroy() |
कॉम्पोनेंट के स्कोप
डिफ़ॉल्ट रूप से, Hilt में सभी बाइंडिंग स्कोप के बाहर होती हैं. इसका मतलब है कि जब भी आपका ऐप्लिकेशन बाइंडिंग का अनुरोध करता है, तो Hilt ज़रूरी टाइप का नया इंस्टेंस बनाता है.
उदाहरण में, जब भी Hilt किसी दूसरे टाइप के लिए या फ़ील्ड इंजेक्शन के ज़रिए (जैसा कि ExampleActivity
में है) AnalyticsAdapter
को डिपेंडेंसी के तौर पर उपलब्ध कराता है, तो Hilt AnalyticsAdapter
का एक नया इंस्टेंस उपलब्ध कराता है.
हालांकि, Hilt की मदद से किसी बाइंडिंग को किसी खास कॉम्पोनेंट के दायरे में भी रखा जा सकता है. Hilt, स्कोप वाली बाइंडिंग को सिर्फ़ उस कॉम्पोनेंट के हर इंस्टेंस के लिए एक बार बनाता है जिसके लिए बाइंडिंग को स्कोप किया गया है. साथ ही, उस बाइंडिंग के सभी अनुरोध एक ही इंस्टेंस शेयर करते हैं.
नीचे दी गई टेबल में, जनरेट किए गए हर कॉम्पोनेंट के लिए स्कोप एनोटेशन की सूची दी गई है:
Android क्लास | जनरेट किया गया कॉम्पोनेंट | दायरा |
---|---|---|
Application |
SingletonComponent |
@Singleton |
Activity |
ActivityRetainedComponent |
@ActivityRetainedScoped |
ViewModel |
ViewModelComponent |
@ViewModelScoped |
Activity |
ActivityComponent |
@ActivityScoped |
Fragment |
FragmentComponent |
@FragmentScoped |
View |
ViewComponent |
@ViewScoped |
View पर @WithFragmentBindings वाला एनोटेशन |
ViewWithFragmentComponent |
@ViewScoped |
Service |
ServiceComponent |
@ServiceScoped |
उदाहरण में, अगर @ActivityScoped
का इस्तेमाल करके AnalyticsAdapter
को ActivityComponent
के दायरे में रखा जाता है, तो Hilt उस गतिविधि के पूरे जीवनकाल में AnalyticsAdapter
का एक ही इंस्टेंस उपलब्ध कराता है:
Kotlin
@ActivityScoped class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
Java
@ActivityScoped public class AnalyticsAdapter { private final AnalyticsService service; @Inject AnalyticsAdapter(AnalyticsService service) { this.service = service; } ... }
मान लें कि AnalyticsService
की एक इंटरनल स्टेटस है, जिसके लिए हर बार एक ही इंस्टेंस का इस्तेमाल करना ज़रूरी है. ऐसा न सिर्फ़ ExampleActivity
में, बल्कि ऐप्लिकेशन में कहीं भी किया जा सकता है. इस मामले में, AnalyticsService
को SingletonComponent
तक सीमित करना सही है. इसका मतलब है कि जब भी कॉम्पोनेंट को AnalyticsService
का कोई इंस्टेंस चाहिए होता है, तो वह हर बार एक ही इंस्टेंस उपलब्ध कराता है.
इस उदाहरण में, Hilt मॉड्यूल में किसी कॉम्पोनेंट के लिए बाइंडिंग का दायरा तय करने का तरीका बताया गया है. बाइंडिंग का दायरा, उस कॉम्पोनेंट के दायरे से मेल खाना चाहिए जहां इसे इंस्टॉल किया गया है. इसलिए, इस उदाहरण में आपको ActivityComponent
के बजाय SingletonComponent
में AnalyticsService
इंस्टॉल करना होगा:
Kotlin
// If AnalyticsService is an interface. @Module @InstallIn(SingletonComponent::class) abstract class AnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService } // If you don't own AnalyticsService. @Module @InstallIn(SingletonComponent::class) object AnalyticsModule { @Singleton @Provides fun provideAnalyticsService(): AnalyticsService { return Retrofit.Builder() .baseUrl("https://example.com") .build() .create(AnalyticsService::class.java) } }
Java
// If AnalyticsService is an interface. @Module @InstallIn(SingletonComponent.class) public abstract class AnalyticsModule { @Singleton @Binds public abstract AnalyticsService bindAnalyticsService( AnalyticsServiceImpl analyticsServiceImpl ); } // If you don't own AnalyticsService. @Module @InstallIn(SingletonComponent.class) public class AnalyticsModule { @Singleton @Provides public static AnalyticsService provideAnalyticsService() { return new Retrofit.Builder() .baseUrl("https://example.com") .build() .create(AnalyticsService.class); } }
Hilt कॉम्पोनेंट के स्कोप के बारे में ज़्यादा जानने के लिए, Android और Hilt में स्कोप करना लेख पढ़ें.
कॉम्पोनेंट की हैरारकी
किसी कॉम्पोनेंट में मॉड्यूल इंस्टॉल करने से, उसकी बाइंडिंग को उस कॉम्पोनेंट में मौजूद अन्य बाइंडिंग या कॉम्पोनेंट के क्रम में उसके नीचे मौजूद किसी चाइल्ड कॉम्पोनेंट की डिपेंडेंसी के तौर पर ऐक्सेस किया जा सकता है:
कॉम्पोनेंट की डिफ़ॉल्ट बाइंडिंग
हर Hilt कॉम्पोनेंट में डिफ़ॉल्ट बाइंडिंग का एक सेट होता है. Hilt, इन बाइंडिंग को आपकी कस्टम बाइंडिंग में डिपेंडेंसी के तौर पर इंजेक्ट कर सकता है. ध्यान दें कि ये बाइंडिंग, सामान्य गतिविधि और फ़्रैगमेंट टाइप से जुड़ी होती हैं, न कि किसी खास सबक्लास से. ऐसा इसलिए है, क्योंकि Hilt सभी गतिविधियों को इंजेक्ट करने के लिए, एक ऐक्टिविटी कॉम्पोनेंट की परिभाषा का इस्तेमाल करता है. हर गतिविधि में इस कॉम्पोनेंट का एक अलग इंस्टेंस होता है.
Android कॉम्पोनेंट | डिफ़ॉल्ट बाइंडिंग |
---|---|
SingletonComponent |
Application |
ActivityRetainedComponent |
Application |
ViewModelComponent |
SavedStateHandle |
ActivityComponent |
Application , Activity |
FragmentComponent |
Application , Activity , Fragment |
ViewComponent |
Application , Activity , View |
ViewWithFragmentComponent |
Application , Activity , Fragment , View |
ServiceComponent |
Application , Service |
@ApplicationContext
का इस्तेमाल करके, ऐप्लिकेशन कॉन्टेक्स्ट बाइंडिंग भी उपलब्ध है.
उदाहरण के लिए:
Kotlin
class AnalyticsServiceImpl @Inject constructor( @ApplicationContext context: Context ) : AnalyticsService { ... } // The Application binding is available without qualifiers. class AnalyticsServiceImpl @Inject constructor( application: Application ) : AnalyticsService { ... }
Java
public class AnalyticsServiceImpl implements AnalyticsService { private final Context context; @Inject AnalyticsAdapter(@ApplicationContext Context context) { this.context = context; } } // The Application binding is available without qualifiers. public class AnalyticsServiceImpl implements AnalyticsService { private final Application application; @Inject AnalyticsAdapter(Application application) { this.application = application; } }
@ActivityContext
का इस्तेमाल करके, ऐक्टिविटी कॉन्टेक्स्ट बाइंडिंग भी उपलब्ध है. उदाहरण के लिए:
Kotlin
class AnalyticsAdapter @Inject constructor( @ActivityContext context: Context ) { ... } // The Activity binding is available without qualifiers. class AnalyticsAdapter @Inject constructor( activity: FragmentActivity ) { ... }
Java
public class AnalyticsAdapter { private final Context context; @Inject AnalyticsAdapter(@ActivityContext Context context) { this.context = context; } } // The Activity binding is available without qualifiers. public class AnalyticsAdapter { private final FragmentActivity activity; @Inject AnalyticsAdapter(FragmentActivity activity) { this.activity = activity; } }
Hilt के साथ काम न करने वाली क्लास में डिपेंडेंसी इंजेक्ट करना
Hilt में, Android की ज़्यादातर सामान्य क्लास के लिए सहायता मिलती है. हालांकि, आपको उन क्लास में फ़ील्ड इंजेक्शन करने की ज़रूरत पड़ सकती है जिनमें Hilt काम नहीं करता.
ऐसे मामलों में, @EntryPoint
एनोटेशन का इस्तेमाल करके एंट्री पॉइंट बनाया जा सकता है. एंट्री पॉइंट, Hilt के ज़रिए मैनेज किए जाने वाले कोड और मैनेज नहीं किए जाने वाले कोड के बीच की सीमा होती है. यह वह पॉइंट होता है जहां कोड, उन ऑब्जेक्ट के ग्राफ़ में पहली बार शामिल होता है जिन्हें Hilt मैनेज करता है. एंट्री पॉइंट की मदद से, Hilt उस कोड का इस्तेमाल कर सकता है जिसे डिपेंडेंसी ग्राफ़ में डिपेंडेंसी के तौर पर शामिल नहीं किया जा सकता.
उदाहरण के लिए, Hilt सीधे तौर पर कॉन्टेंट उपलब्ध कराने वाली कंपनियों के साथ काम नहीं करता. अगर आपको कॉन्टेंट की सेवा देने वाली कंपनी को कुछ डिपेंडेंसी पाने के लिए Hilt का इस्तेमाल करना है, तो आपको एक ऐसा इंटरफ़ेस तय करना होगा जिसे आपके पसंदीदा बाइंडिंग टाइप के लिए @EntryPoint
के साथ एनोटेट किया गया हो. साथ ही, इसमें क्वालीफ़ायर शामिल करें. इसके बाद, @InstallIn
जोड़ें, ताकि यह तय किया जा सके कि एंट्री पॉइंट को किस कंपोनेंट में इंस्टॉल करना है. इसके लिए, यह तरीका अपनाएं:
Kotlin
class ExampleContentProvider : ContentProvider() { @EntryPoint @InstallIn(SingletonComponent::class) interface ExampleContentProviderEntryPoint { fun analyticsService(): AnalyticsService } ... }
Java
public class ExampleContentProvider extends ContentProvider { @EntryPoint @InstallIn(SingletonComponent.class) interface ExampleContentProviderEntryPoint { public AnalyticsService analyticsService(); } ... }
एंट्री पॉइंट को ऐक्सेस करने के लिए, EntryPointAccessors
में मौजूद सही स्टैटिक तरीके का इस्तेमाल करें. पैरामीटर, कॉम्पोनेंट इंस्टेंस या @AndroidEntryPoint
ऑब्जेक्ट होना चाहिए, जो कॉम्पोनेंट होल्डर के तौर पर काम करता है. पक्का करें कि पैरामीटर के तौर पर पास किया गया कॉम्पोनेंट और EntryPointAccessors
स्टैटिक तरीका, दोनों @EntryPoint
इंटरफ़ेस पर @InstallIn
एनोटेशन में मौजूद Android क्लास से मेल खाते हों:
Kotlin
class ExampleContentProvider: ContentProvider() { ... override fun query(...): Cursor { val appContext = context?.applicationContext ?: throw IllegalStateException() val hiltEntryPoint = EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint::class.java) val analyticsService = hiltEntryPoint.analyticsService() ... } }
Java
public class ExampleContentProvider extends ContentProvider { @Override public Cursor query(...) { Context appContext = getContext().getApplicationContext(); ExampleContentProviderEntryPoint hiltEntryPoint = EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint.class); AnalyticsService analyticsService = hiltEntryPoint.analyticsService(); } }
इस उदाहरण में, एंट्री पॉइंट को वापस पाने के लिए, आपको ApplicationContext
का इस्तेमाल करना होगा, क्योंकि एंट्री पॉइंट SingletonComponent
में इंस्टॉल है. अगर आपको ActivityComponent
में मौजूद बाइंडिंग को वापस पाना है, तो इसके लिए ActivityContext
का इस्तेमाल करें.
हिल्ट ऐंड डैगर
Hilt, Dagger डिपेंडेंसी इंजेक्शन लाइब्रेरी के ऊपर बनाई गई है. इससे, Android ऐप्लिकेशन में Dagger को शामिल करने का स्टैंडर्ड तरीका मिलता है.
Dagger के मुकाबले, Hilt के लक्ष्य ये हैं:
- Android ऐप्लिकेशन के लिए, Dagger से जुड़े इंफ़्रास्ट्रक्चर को आसान बनाना.
- कॉम्पोनेंट और स्कोप का स्टैंडर्ड सेट बनाने के लिए, ताकि ऐप्लिकेशन के बीच सेटअप करने, पढ़ने, और कोड शेयर करने में आसानी हो.
- अलग-अलग तरह के बिल्ड के लिए, अलग-अलग बाइंडिंग को आसानी से प्रोवाइड करने के लिए. जैसे, टेस्टिंग, डीबग या रिलीज़.
Android ऑपरेटिंग सिस्टम, अपने फ़्रेमवर्क की कई क्लास को इंस्टैंशिएट करता है. इसलिए, Android ऐप्लिकेशन में Dagger का इस्तेमाल करने के लिए, आपको काफ़ी बोइलरप्लेट लिखना पड़ता है. Hilt, Android ऐप्लिकेशन में Dagger का इस्तेमाल करने के लिए, छोटे-मोटे बदलाव वाले कोड को कम करता है. Hilt, ये चीज़ें अपने-आप जनरेट करता है और उपलब्ध कराता है:
- Dagger के साथ Android फ़्रेमवर्क क्लास को इंटिग्रेट करने के लिए कॉम्पोनेंट, जिन्हें आपको मैन्युअल तरीके से बनाना पड़ता.
- स्कोप एनोटेशन, जिन्हें Hilt के अपने-आप जनरेट होने वाले कॉम्पोनेंट के साथ इस्तेमाल किया जा सकता है.
Application
याActivity
जैसी Android क्लास दिखाने के लिए, पहले से तय किए गए बाइंडिंग.@ApplicationContext
और@ActivityContext
को दिखाने के लिए, पहले से तय किए गए क्वालिफ़ायर.
Dagger और Hilt कोड, एक ही कोडबेस में एक साथ काम कर सकते हैं. हालांकि, ज़्यादातर मामलों में, Android पर Dagger के इस्तेमाल को मैनेज करने के लिए, Hilt का इस्तेमाल करना सबसे सही होता है. Dagger का इस्तेमाल करने वाले किसी प्रोजेक्ट को Hilt पर माइग्रेट करने के लिए, डेटा को दूसरी जगह भेजने से जुड़ी गाइड और अपने Dagger ऐप्लिकेशन को Hilt पर माइग्रेट करने से जुड़ा कोडलैब देखें.
अन्य संसाधन
Hilt के बारे में ज़्यादा जानने के लिए, यहां दिए गए अन्य रिसॉर्स देखें.
सैंपल
कोडलैब
ब्लॉग
- Hilt की मदद से, Android पर डिपेंडेंसी इंजेक्शन
- Android और Hilt में स्कोप करना
- Hilt की हैरारकी में कॉम्पोनेंट जोड़ना
- Google I/O ऐप्लिकेशन को Hilt पर माइग्रेट करना