হিল্ট হলো অ্যান্ড্রয়েডের জন্য একটি ডিপেন্ডেন্সি ইনজেকশন লাইব্রেরি, যা আপনার প্রোজেক্টে ম্যানুয়াল ডিপেন্ডেন্সি ইনজেকশন করার গতানুগতিক কাজগুলো কমিয়ে দেয়। ম্যানুয়াল ডিপেন্ডেন্সি ইনজেকশন করতে গেলে প্রতিটি ক্লাস এবং তার ডিপেন্ডেন্সিগুলো হাতে তৈরি করতে হয় এবং ডিপেন্ডেন্সিগুলো পুনঃব্যবহার ও পরিচালনা করার জন্য কন্টেইনার ব্যবহার করতে হয়।
Hilt আপনার প্রোজেক্টের প্রতিটি অ্যান্ড্রয়েড ক্লাসের জন্য কন্টেইনার সরবরাহ করে এবং স্বয়ংক্রিয়ভাবে তাদের লাইফসাইকেল পরিচালনা করার মাধ্যমে আপনার অ্যাপ্লিকেশনে DI ব্যবহারের একটি আদর্শ উপায় প্রদান করে। Hilt জনপ্রিয় DI লাইব্রেরি Dagger- এর উপর ভিত্তি করে তৈরি, যাতে Dagger-এর প্রদান করা কম্পাইল-টাইম সঠিকতা, রানটাইম পারফরম্যান্স, স্কেলেবিলিটি এবং অ্যান্ড্রয়েড স্টুডিও সমর্থনের সুবিধাগুলো পাওয়া যায়। আরও তথ্যের জন্য, Hilt এবং Dagger দেখুন।
এই নির্দেশিকাটি Hilt এবং এর দ্বারা তৈরি কন্টেইনারগুলির মৌলিক ধারণা ব্যাখ্যা করে। এতে একটি বিদ্যমান অ্যাপকে Hilt ব্যবহার করার জন্য কীভাবে বুটস্ট্র্যাপ করতে হয় তার একটি প্রদর্শনীও অন্তর্ভুক্ত রয়েছে।
নির্ভরতা যোগ করা
প্রথমে, আপনার প্রোজেক্টের রুট build.gradle ফাইলে hilt-android-gradle-plugin প্লাগইনটি যোগ করুন:
গ্রুভি
plugins { ... id 'com.google.dagger.hilt.android' version '2.57.1' apply false }
কোটলিন
plugins { ... id("com.google.dagger.hilt.android") version "2.57.1" apply false }
এরপর, গ্রেডল প্লাগইনটি প্রয়োগ করুন এবং আপনার app/build.gradle ফাইলে এই ডিপেন্ডেন্সিগুলো যোগ করুন:
গ্রুভি
... plugins { id 'com.google.devtools.ksp' id 'com.google.dagger.hilt.android' } android { ... } dependencies { implementation "com.google.dagger:hilt-android:2.57.1" ksp "com.google.dagger:hilt-compiler:2.57.1" }
কোটলিন
plugins { id("com.google.devtools.ksp") id("com.google.dagger.hilt.android") } android { ... } dependencies { implementation("com.google.dagger:hilt-android:2.57.1") ksp("com.google.dagger:hilt-android-compiler:2.57.1") }
Hilt জাভা ৮-এর বৈশিষ্ট্য ব্যবহার করে। আপনার প্রোজেক্টে জাভা ৮ সক্রিয় করতে, app/build.gradle ফাইলে নিম্নলিখিতটি যোগ করুন:
গ্রুভি
android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } }
কোটলিন
android { ... compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } }
হিল্ট অ্যাপ্লিকেশন ক্লাস
যেসব অ্যাপে Hilt ব্যবহৃত হয়, সেগুলোতে অবশ্যই @HiltAndroidApp অ্যানোটেশনযুক্ত একটি Application ক্লাস থাকতে হবে।
@HiltAndroidApp Hilt-এর কোড জেনারেশন চালু করে, যার মধ্যে আপনার অ্যাপ্লিকেশনের জন্য একটি বেস ক্লাস অন্তর্ভুক্ত থাকে, যা অ্যাপ্লিকেশন-স্তরের ডিপেন্ডেন্সি কন্টেইনার হিসেবে কাজ করে।
কোটলিন
@HiltAndroidApp class ExampleApplication : Application() { ... }
জাভা
@HiltAndroidApp public class ExampleApplication extends Application { ... }
এই জেনারেট করা Hilt কম্পোনেন্টটি Application অবজেক্টের লাইফসাইকেলের সাথে সংযুক্ত থাকে এবং এটিকে ডিপেন্ডেন্সি সরবরাহ করে। এছাড়াও, এটি অ্যাপটির প্যারেন্ট কম্পোনেন্ট, যার অর্থ হলো অন্যান্য কম্পোনেন্টগুলো এর সরবরাহ করা ডিপেন্ডেন্সিগুলো অ্যাক্সেস করতে পারে।
অ্যান্ড্রয়েড ক্লাসে নির্ভরতা প্রবেশ করান
আপনার Application ক্লাসে Hilt সেট আপ করা হয়ে গেলে এবং একটি অ্যাপ্লিকেশন-স্তরের কম্পোনেন্ট উপলব্ধ থাকলে, Hilt সেইসব Android ক্লাসকে ডিপেন্ডেন্সি প্রদান করতে পারে যেগুলিতে @AndroidEntryPoint অ্যানোটেশন রয়েছে:
কোটলিন
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { ... }
জাভা
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { ... }
হিল্ট বর্তমানে নিম্নলিখিত অ্যান্ড্রয়েড ক্লাসগুলিকে সমর্থন করে:
-
Application(@HiltAndroidAppব্যবহার করে) -
ViewModel(@HiltViewModelব্যবহার করে) -
Activity -
Fragment -
View -
Service -
BroadcastReceiver
যদি আপনি কোনো অ্যান্ড্রয়েড ক্লাসকে @AndroidEntryPoint দিয়ে অ্যানোটেট করেন, তাহলে আপনাকে অবশ্যই সেই ক্লাসের উপর নির্ভরশীল অ্যান্ড্রয়েড ক্লাসগুলোকেও অ্যানোটেট করতে হবে। উদাহরণস্বরূপ, যদি আপনি কোনো ফ্র্যাগমেন্টকে অ্যানোটেট করেন, তাহলে আপনাকে অবশ্যই সেই ফ্র্যাগমেন্টটি ব্যবহার করা যেকোনো অ্যাক্টিভিটিকেও অ্যানোটেট করতে হবে।
@AndroidEntryPoint আপনার প্রোজেক্টের প্রতিটি অ্যান্ড্রয়েড ক্লাসের জন্য একটি স্বতন্ত্র Hilt কম্পোনেন্ট তৈরি করে। কম্পোনেন্ট হায়ারার্কিতে বর্ণিত নিয়ম অনুযায়ী এই কম্পোনেন্টগুলো তাদের নিজ নিজ প্যারেন্ট ক্লাস থেকে ডিপেন্ডেন্সি গ্রহণ করতে পারে।
কোনো কম্পোনেন্ট থেকে ডিপেন্ডেন্সি পেতে, ফিল্ড ইনজেকশন সম্পাদন করার জন্য @Inject অ্যানোটেশনটি ব্যবহার করুন:
কোটলিন
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { @Inject lateinit var analytics: AnalyticsAdapter ... }
জাভা
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { @Inject AnalyticsAdapter analytics; ... }
যেসব ক্লাসে হিল্ট ইনজেক্ট করে, সেগুলোর অন্যান্য বেস ক্লাসও থাকতে পারে যারা ইনজেকশন ব্যবহার করে। সেই ক্লাসগুলো অ্যাবস্ট্রাক্ট হলে সেগুলোতে @AndroidEntryPoint অ্যানোটেশনের প্রয়োজন হয় না।
একটি অ্যান্ড্রয়েড ক্লাস কোন লাইফসাইকেল কলব্যাকে ইনজেক্ট করা হয় সে সম্পর্কে আরও জানতে, কম্পোনেন্ট লাইফটাইমস দেখুন।
হিল্ট বাইন্ডিং সংজ্ঞায়িত করুন
ফিল্ড ইনজেকশন সম্পাদন করার জন্য, হিল্টকে জানতে হয় কীভাবে সংশ্লিষ্ট কম্পোনেন্ট থেকে প্রয়োজনীয় ডিপেন্ডেন্সিগুলোর ইনস্ট্যান্স সরবরাহ করতে হয়। একটি বাইন্ডিং-এ কোনো টাইপের ইনস্ট্যান্সকে ডিপেন্ডেন্সি হিসেবে সরবরাহ করার জন্য প্রয়োজনীয় তথ্য থাকে।
Hilt-কে বাইন্ডিং তথ্য দেওয়ার একটি উপায় হলো কনস্ট্রাক্টর ইনজেকশন । কোনো ক্লাসের ইনস্ট্যান্স কীভাবে সরবরাহ করতে হবে, তা Hilt-কে জানানোর জন্য সেই ক্লাসের কনস্ট্রাক্টরে @Inject অ্যানোটেশনটি ব্যবহার করুন:
কোটলিন
class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
জাভা
public class AnalyticsAdapter { private final AnalyticsService service; @Inject AnalyticsAdapter(AnalyticsService service) { this.service = service; } ... }
একটি ক্লাসের অ্যানোটেড কনস্ট্রাক্টরের প্যারামিটারগুলো হলো সেই ক্লাসের ডিপেন্ডেন্সিগুলো। উদাহরণস্বরূপ, AnalyticsAdapter একটি ডিপেন্ডেন্সি হলো AnalyticsService । সুতরাং, Hilt-কে অবশ্যই জানতে হবে কীভাবে AnalyticsService এর ইনস্ট্যান্স সরবরাহ করতে হয়।
হিল্ট মডিউল
কখনও কখনও একটি টাইপ কনস্ট্রাক্টর-ইনজেক্ট করা যায় না। এটি বিভিন্ন কারণে ঘটতে পারে। উদাহরণস্বরূপ, আপনি একটি ইন্টারফেস কনস্ট্রাক্টর-ইনজেক্ট করতে পারবেন না। এছাড়াও, আপনি এমন কোনো টাইপ কনস্ট্রাক্টর-ইনজেক্ট করতে পারবেন না যার মালিক আপনি নন, যেমন কোনো এক্সটার্নাল লাইব্রেরির ক্লাস। এইসব ক্ষেত্রে, আপনি Hilt মডিউল ব্যবহার করে Hilt-কে বাইন্ডিং তথ্য সরবরাহ করতে পারেন।
একটি হিল্ট মডিউল হলো এমন একটি ক্লাস যা @Module দিয়ে অ্যানোটেট করা থাকে। ড্যাগার মডিউলের মতোই, এটি হিল্টকে জানায় কীভাবে নির্দিষ্ট ধরনের ইনস্ট্যান্স সরবরাহ করতে হবে। ড্যাগার মডিউলের থেকে ভিন্ন, হিল্ট মডিউলগুলোকে অবশ্যই @InstallIn দিয়ে অ্যানোটেট করতে হবে, যাতে হিল্ট জানতে পারে প্রতিটি মডিউল কোন অ্যান্ড্রয়েড ক্লাসে ব্যবহৃত বা ইনস্টল করা হবে।
আপনি Hilt মডিউলে যে ডিপেন্ডেন্সিগুলো প্রদান করেন, সেগুলো সেই সমস্ত জেনারেটেড কম্পোনেন্টে উপলব্ধ থাকে যা সেই Android ক্লাসের সাথে যুক্ত থাকে যেখানে আপনি Hilt মডিউলটি ইনস্টল করেন।
@Binds ব্যবহার করে ইন্টারফেস ইনস্ট্যান্স ইনজেক্ট করুন
AnalyticsService উদাহরণটি বিবেচনা করুন। যদি AnalyticsService একটি ইন্টারফেস হয়, তাহলে আপনি এটিকে কনস্ট্রাক্টর-ইনজেক্ট করতে পারবেন না। এর পরিবর্তে, একটি Hilt মডিউলের ভিতরে @Binds দিয়ে টীকাযুক্ত একটি অ্যাবস্ট্রাক্ট ফাংশন তৈরি করে Hilt-কে বাইন্ডিং তথ্য সরবরাহ করুন।
@Binds অ্যানোটেশনটি Hilt-কে বলে দেয় যে, যখন কোনো ইন্টারফেসের ইনস্ট্যান্স সরবরাহ করার প্রয়োজন হয়, তখন কোন ইমপ্লিমেন্টেশনটি ব্যবহার করতে হবে।
টীকাযুক্ত ফাংশনটি হিল্টকে নিম্নলিখিত তথ্য প্রদান করে:
- ফাংশনের রিটার্ন টাইপ Hilt-কে বলে দেয় যে ফাংশনটি কোন ইন্টারফেসের ইনস্ট্যান্স সরবরাহ করে।
- ফাংশন প্যারামিটারটি হিল্টকে বলে দেয় কোন ইমপ্লিমেন্টেশনটি প্রদান করতে হবে।
কোটলিন
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 }
জাভা
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 মডিউলের ভিতরে একটি ফাংশন তৈরি করে এবং সেই ফাংশনটিকে @Provides দিয়ে অ্যানোটেট করার মাধ্যমে Hilt-কে বলে দিতে পারেন যে এই ধরনের ইনস্ট্যান্স কীভাবে সরবরাহ করতে হবে।
টীকাযুক্ত ফাংশনটি হিল্টকে নিম্নলিখিত তথ্য সরবরাহ করে:
- ফাংশনের রিটার্ন টাইপ হিল্টকে বলে দেয় যে ফাংশনটি কোন ধরনের ইনস্ট্যান্স সরবরাহ করে।
- ফাংশন প্যারামিটারগুলো হিল্টকে সংশ্লিষ্ট টাইপের নির্ভরতাগুলো সম্পর্কে জানায়।
- ফাংশন বডি হিল্টকে বলে দেয় যে সংশ্লিষ্ট টাইপের একটি ইনস্ট্যান্স কীভাবে সরবরাহ করতে হবে। যখনই সেই টাইপের একটি ইনস্ট্যান্স সরবরাহ করার প্রয়োজন হয়, হিল্ট ফাংশন বডিটি এক্সিকিউট করে।
কোটলিন
@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) } }
জাভা
@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 অবজেক্ট ব্যবহার করতে পারেন। অন্যান্য সার্ভিসের ক্ষেত্রে, আপনার হয়তো ভিন্ন উপায়ে কলগুলো ইন্টারসেপ্ট করার প্রয়োজন হতে পারে। সেক্ষেত্রে, OkHttpClient এর দুটি ভিন্ন ইমপ্লিমেন্টেশন কীভাবে সরবরাহ করতে হবে, তা আপনাকে Hilt-কে বলে দিতে হবে।
প্রথমে, @Binds বা @Provides মেথডগুলোকে টীকাযুক্ত করতে আপনি যে কোয়ালিফায়ারগুলো ব্যবহার করবেন, সেগুলো সংজ্ঞায়িত করুন:
কোটলিন
@Qualifier @Retention(AnnotationRetention.BINARY) annotation class AuthInterceptorOkHttpClient @Qualifier @Retention(AnnotationRetention.BINARY) annotation class OtherInterceptorOkHttpClient
জাভা
@Qualifier @Retention(RetentionPolicy.RUNTIME) private @interface AuthInterceptorOkHttpClient {} @Qualifier @Retention(RetentionPolicy.RUNTIME) private @interface OtherInterceptorOkHttpClient {}
এরপর, প্রতিটি কোয়ালিফায়ারের সাথে সঙ্গতিপূর্ণ টাইপের একটি ইনস্ট্যান্স কীভাবে সরবরাহ করতে হয়, তা Hilt-কে জানতে হবে। এক্ষেত্রে, আপনি @Provides সহ একটি Hilt মডিউল ব্যবহার করতে পারেন। উভয় মেথডের রিটার্ন টাইপ একই, কিন্তু কোয়ালিফায়ারগুলো সেগুলোকে দুটি ভিন্ন বাইন্ডিং হিসেবে চিহ্নিত করে:
কোটলিন
@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() } }
জাভা
@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(); } }
আপনি ফিল্ড বা প্যারামিটারকে সংশ্লিষ্ট কোয়ালিফায়ার দিয়ে টীকাযুক্ত করে আপনার প্রয়োজনীয় নির্দিষ্ট টাইপটি অন্তর্ভুক্ত করতে পারেন:
কোটলিন
// 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 }
জাভা
// 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 কিছু পূর্বনির্ধারিত কোয়ালিফায়ার প্রদান করে। উদাহরণস্বরূপ, যেহেতু আপনার অ্যাপ্লিকেশন বা অ্যাক্টিভিটি উভয় জায়গা থেকেই Context ক্লাসের প্রয়োজন হতে পারে, তাই Hilt @ApplicationContext এবং @ActivityContext কোয়ালিফায়ারগুলো সরবরাহ করে।
ধরা যাক, উদাহরণে দেওয়া AnalyticsAdapter ক্লাসটির অ্যাক্টিভিটির কনটেক্সট প্রয়োজন। নিচের কোডটিতে দেখানো হয়েছে কিভাবে AnalyticsAdapter কে অ্যাক্টিভিটি কনটেক্সট সরবরাহ করতে হয়:
কোটলিন
class AnalyticsAdapter @Inject constructor( @ActivityContext private val context: Context, private val service: AnalyticsService ) { ... }
জাভা
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-এ উপলব্ধ অন্যান্য পূর্বনির্ধারিত বাইন্ডিংগুলির জন্য, কম্পোনেন্ট ডিফল্ট বাইন্ডিং দেখুন।
অ্যান্ড্রয়েড ক্লাসের জন্য তৈরি উপাদান
প্রতিটি অ্যান্ড্রয়েড ক্লাসের জন্য, যেখানে আপনি ফিল্ড ইনজেকশন করতে পারেন, একটি সংশ্লিষ্ট Hilt কম্পোনেন্ট থাকে, যা আপনি @InstallIn অ্যানোটেশনে উল্লেখ করতে পারেন। প্রতিটি Hilt কম্পোনেন্ট তার বাইন্ডিংগুলোকে সংশ্লিষ্ট অ্যান্ড্রয়েড ক্লাসে ইনজেক্ট করার জন্য দায়ী থাকে।
পূর্ববর্তী উদাহরণগুলিতে হিল্ট মডিউলে ActivityComponent ব্যবহার দেখানো হয়েছে।
হিল্ট নিম্নলিখিত উপাদানগুলি সরবরাহ করে:
| হিল্ট উপাদান | ইনজেক্টরের জন্য |
|---|---|
SingletonComponent | Application |
ActivityRetainedComponent | প্রযোজ্য নয় |
ViewModelComponent | ViewModel |
ActivityComponent | Activity |
FragmentComponent | Fragment |
ViewComponent | View |
ViewWithFragmentComponent | @WithFragmentBindings দিয়ে টীকাযুক্ত View |
ServiceComponent | Service |
উপাদানের জীবনকাল
Hilt স্বয়ংক্রিয়ভাবে সংশ্লিষ্ট অ্যান্ড্রয়েড ক্লাসগুলির জীবনচক্র অনুসরণ করে জেনারেট করা কম্পোনেন্ট ক্লাসগুলির ইনস্ট্যান্স তৈরি ও ধ্বংস করে।
| উত্পাদিত উপাদান | তৈরি করা হয়েছে | ধ্বংস করা হয়েছে |
|---|---|---|
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 শুধুমাত্র একবারই সেই কম্পোনেন্টের প্রতিটি ইনস্ট্যান্সের জন্য একটি স্কোপড বাইন্ডিং তৈরি করে, এবং সেই বাইন্ডিংয়ের জন্য করা সমস্ত অনুরোধ একই ইনস্ট্যান্স ব্যবহার করে।
নিচের সারণিতে প্রতিটি জেনারেট করা কম্পোনেন্টের স্কোপ অ্যানোটেশনগুলো তালিকাভুক্ত করা হয়েছে:
| অ্যান্ড্রয়েড ক্লাস | উত্পাদিত উপাদান | পরিধি |
|---|---|---|
Application | SingletonComponent | @Singleton |
Activity | ActivityRetainedComponent | @ActivityRetainedScoped |
ViewModel | ViewModelComponent | @ViewModelScoped |
Activity | ActivityComponent | @ActivityScoped |
Fragment | FragmentComponent | @FragmentScoped |
View | ViewComponent | @ViewScoped |
@WithFragmentBindings দিয়ে টীকাযুক্ত View | ViewWithFragmentComponent | @ViewScoped |
Service | ServiceComponent | @ServiceScoped |
উদাহরণে, যদি আপনি @ActivityScoped ব্যবহার করে AnalyticsAdapter ActivityComponent এর মধ্যে সীমাবদ্ধ করেন, তাহলে Hilt সংশ্লিষ্ট অ্যাক্টিভিটির পুরো জীবনকাল জুড়ে AnalyticsAdapter এর একই ইনস্ট্যান্স সরবরাহ করে:
কোটলিন
@ActivityScoped class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
জাভা
@ActivityScoped public class AnalyticsAdapter { private final AnalyticsService service; @Inject AnalyticsAdapter(AnalyticsService service) { this.service = service; } ... }
ধরা যাক, AnalyticsService এমন একটি অভ্যন্তরীণ অবস্থা আছে যার জন্য প্রতিবার একই ইনস্ট্যান্স ব্যবহার করা প্রয়োজন—শুধু ExampleActivity তেই নয়, অ্যাপের যেকোনো জায়গায়। এই ক্ষেত্রে, AnalyticsService SingletonComponent এর স্কোপের মধ্যে রাখা উপযুক্ত। এর ফলে, যখনই কম্পোনেন্টটির AnalyticsService এর একটি ইনস্ট্যান্স সরবরাহ করার প্রয়োজন হয়, এটি প্রতিবার একই ইনস্ট্যান্স সরবরাহ করে।
নিম্নলিখিত উদাহরণটি দেখায় কিভাবে একটি Hilt মডিউলে কোনো কম্পোনেন্টের জন্য একটি বাইন্ডিং-এর স্কোপ নির্ধারণ করতে হয়। একটি বাইন্ডিং-এর স্কোপ অবশ্যই সেই কম্পোনেন্টের স্কোপের সাথে মিলতে হবে যেখানে এটি ইনস্টল করা হয়েছে, তাই এই উদাহরণে আপনাকে ActivityComponent এর পরিবর্তে SingletonComponent এ AnalyticsService ইনস্টল করতে হবে:
কোটলিন
// 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) } }
জাভা
// 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 সমস্ত অ্যাক্টিভিটি ইনজেক্ট করার জন্য একটিমাত্র অ্যাক্টিভিটি কম্পোনেন্ট ডেফিনিশন ব্যবহার করে। প্রতিটি অ্যাক্টিভিটিতে এই কম্পোনেন্টের একটি ভিন্ন ইনস্ট্যান্স থাকে।
| অ্যান্ড্রয়েড উপাদান | ডিফল্ট বাইন্ডিং |
|---|---|
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 ব্যবহার করেও অ্যাপ্লিকেশন কনটেক্সট বাইন্ডিং করা যায়। উদাহরণস্বরূপ:
কোটলিন
class AnalyticsServiceImpl @Inject constructor( @ApplicationContext context: Context ) : AnalyticsService { ... } // The Application binding is available without qualifiers. class AnalyticsServiceImpl @Inject constructor( application: Application ) : AnalyticsService { ... }
জাভা
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 ব্যবহার করেও অ্যাক্টিভিটি কনটেক্সট বাইন্ডিং করা যায়। উদাহরণস্বরূপ:
কোটলিন
class AnalyticsAdapter @Inject constructor( @ActivityContext context: Context ) { ... } // The Activity binding is available without qualifiers. class AnalyticsAdapter @Inject constructor( activity: FragmentActivity ) { ... }
জাভা
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 সমর্থন করে না।
সেই ক্ষেত্রে, আপনি @EntryPoint অ্যানোটেশন ব্যবহার করে একটি এন্ট্রি পয়েন্ট তৈরি করতে পারেন। এন্ট্রি পয়েন্ট হলো Hilt দ্বারা পরিচালিত কোড এবং অপরিচালিত কোডের মধ্যকার সীমানা। এটি সেই বিন্দু যেখানে কোড সর্বপ্রথম Hilt দ্বারা পরিচালিত অবজেক্টের গ্রাফে প্রবেশ করে। এন্ট্রি পয়েন্টগুলো Hilt-কে এমন কোড ব্যবহার করার সুযোগ দেয় যা সে নিজে পরিচালনা করে না, এবং এর মাধ্যমে ডিপেন্ডেন্সি গ্রাফের মধ্যে ডিপেন্ডেন্সি সরবরাহ করে।
উদাহরণস্বরূপ, Hilt সরাসরি কন্টেন্ট প্রোভাইডার সমর্থন করে না। যদি আপনি চান যে কোনো কন্টেন্ট প্রোভাইডার Hilt ব্যবহার করে কিছু ডিপেন্ডেন্সি গ্রহণ করুক, তাহলে আপনার কাঙ্ক্ষিত প্রতিটি বাইন্ডিং টাইপের জন্য @EntryPoint দিয়ে অ্যানোটেটেড একটি ইন্টারফেস সংজ্ঞায়িত করতে হবে এবং কোয়ালিফায়ার অন্তর্ভুক্ত করতে হবে। তারপর, এন্ট্রি পয়েন্টটি কোন কম্পোনেন্টে ইনস্টল করতে হবে তা নির্দিষ্ট করার জন্য @InstallIn যোগ করুন, যেমনটি নিচে দেখানো হলো:
কোটলিন
class ExampleContentProvider : ContentProvider() { @EntryPoint @InstallIn(SingletonComponent::class) interface ExampleContentProviderEntryPoint { fun analyticsService(): AnalyticsService } ... }
জাভা
public class ExampleContentProvider extends ContentProvider { @EntryPoint @InstallIn(SingletonComponent.class) interface ExampleContentProviderEntryPoint { public AnalyticsService analyticsService(); } ... }
একটি এন্ট্রি পয়েন্ট অ্যাক্সেস করতে, EntryPointAccessors থেকে উপযুক্ত স্ট্যাটিক মেথডটি ব্যবহার করুন। প্যারামিটারটি হয় কম্পোনেন্ট ইনস্ট্যান্স অথবা @AndroidEntryPoint অবজেক্ট হওয়া উচিত, যা কম্পোনেন্ট হোল্ডার হিসেবে কাজ করে। নিশ্চিত করুন যে, আপনি প্যারামিটার হিসেবে যে কম্পোনেন্টটি পাস করছেন এবং EntryPointAccessors স্ট্যাটিক মেথড, উভয়ই যেন @EntryPoint ইন্টারফেসের @InstallIn অ্যানোটেশনে থাকা Android ক্লাসের সাথে মেলে।
কোটলিন
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() ... } }
জাভা
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 ডিপেন্ডেন্সি ইনজেকশন লাইব্রেরির উপর ভিত্তি করে নির্মিত, যা একটি অ্যান্ড্রয়েড অ্যাপ্লিকেশনে Dagger অন্তর্ভুক্ত করার একটি আদর্শ উপায় প্রদান করে।
ড্যাগারের ক্ষেত্রে হিল্টের লক্ষ্যগুলো নিম্নরূপ:
- অ্যান্ড্রয়েড অ্যাপের জন্য ড্যাগার-সম্পর্কিত পরিকাঠামো সরলীকরণ করতে।
- অ্যাপগুলোর মধ্যে সেটআপ, পঠনযোগ্যতা এবং কোড শেয়ারিং সহজ করার জন্য কম্পোনেন্ট ও স্কোপের একটি স্ট্যান্ডার্ড সেট তৈরি করা।
- টেস্টিং, ডিবাগ বা রিলিজের মতো বিভিন্ন বিল্ড টাইপের জন্য আলাদা আলাদা বাইন্ডিং সহজে সরবরাহ করার একটি উপায় প্রদান করা।
যেহেতু অ্যান্ড্রয়েড অপারেটিং সিস্টেম তার নিজস্ব অনেক ফ্রেমওয়ার্ক ক্লাস ইনস্ট্যানশিয়েট করে, তাই একটি অ্যান্ড্রয়েড অ্যাপে ড্যাগার ব্যবহার করতে গেলে আপনাকে প্রচুর পরিমাণে বয়লারপ্লেট কোড লিখতে হয়। হিল্ট একটি অ্যান্ড্রয়েড অ্যাপ্লিকেশনে ড্যাগার ব্যবহারের সাথে জড়িত বয়লারপ্লেট কোড কমিয়ে দেয়। হিল্ট স্বয়ংক্রিয়ভাবে নিম্নলিখিত বিষয়গুলো তৈরি করে এবং সরবরাহ করে:
- ড্যাগারের সাথে অ্যান্ড্রয়েড ফ্রেমওয়ার্ক ক্লাসগুলিকে একীভূত করার জন্য এমন সব উপাদান , যা অন্যথায় আপনাকে হাতে তৈরি করতে হতো।
- হিল্ট দ্বারা স্বয়ংক্রিয়ভাবে তৈরি কম্পোনেন্টগুলোর সাথে ব্যবহার করার জন্য স্কোপ অ্যানোটেশন ।
-
ApplicationবাActivityমতো অ্যান্ড্রয়েড ক্লাসগুলোকে উপস্থাপন করার জন্য পূর্বনির্ধারিত বাইন্ডিং । -
@ApplicationContextএবং@ActivityContextউপস্থাপন করার জন্য পূর্বনির্ধারিত কোয়ালিফায়ার ।
একই কোডবেসে ড্যাগার এবং হিল্ট কোড একসাথে থাকতে পারে। তবে, বেশিরভাগ ক্ষেত্রে অ্যান্ড্রয়েডে আপনার ড্যাগারের সমস্ত ব্যবহার পরিচালনা করার জন্য হিল্ট ব্যবহার করাই শ্রেয়। ড্যাগার ব্যবহার করে এমন কোনো প্রজেক্ট হিল্টে মাইগ্রেট করতে, মাইগ্রেশন গাইড এবং ‘আপনার ড্যাগার অ্যাপকে হিল্টে মাইগ্রেট করা’ কোডল্যাবটি দেখুন।
অতিরিক্ত সম্পদ
হিল্ট সম্পর্কে আরও জানতে, নিম্নলিখিত অতিরিক্ত তথ্যসূত্রগুলো দেখুন।
নমুনা
কোডল্যাবস
ব্লগ
- হিল্টের সাহায্যে অ্যান্ড্রয়েডে ডিপেন্ডেন্সি ইনজেকশন
- অ্যান্ড্রয়েড এবং হিল্টে স্কোপিং
- হিল্ট হায়ারার্কিতে উপাদান যোগ করা
- Google I/O অ্যাপটিকে Hilt-এ স্থানান্তর করা হচ্ছে