হিল্টের সাথে নির্ভরতা ইনজেকশন

হিল্ট হলো অ্যান্ড্রয়েডের জন্য একটি ডিপেন্ডেন্সি ইনজেকশন লাইব্রেরি, যা আপনার প্রোজেক্টে ম্যানুয়াল ডিপেন্ডেন্সি ইনজেকশন করার গতানুগতিক কাজগুলো কমিয়ে দেয়। ম্যানুয়াল ডিপেন্ডেন্সি ইনজেকশন করতে গেলে প্রতিটি ক্লাস এবং তার ডিপেন্ডেন্সিগুলো হাতে তৈরি করতে হয় এবং ডিপেন্ডেন্সিগুলো পুনঃব্যবহার ও পরিচালনা করার জন্য কন্টেইনার ব্যবহার করতে হয়।

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-android-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-compiler:2.57.1"
}

আপনার প্রজেক্টটি জাভা ১৭-এর জন্য কনফিগার করা আছে কিনা তা নিশ্চিত করতে, যা Jetpack Compose এবং Hilt ভার্সনগুলোর জন্য আবশ্যক, app/build.gradle ফাইলে নিম্নলিখিত লাইনটি যোগ করুন:

কোটলিন

android {
  ...
  compileOptions {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
  }
}

গ্রুভি

android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_17
    targetCompatibility JavaVersion.VERSION_17
  }
}

হিল্ট অ্যাপ্লিকেশন ক্লাস

যেসব অ্যাপে Hilt ব্যবহৃত হয়, সেগুলোতে অবশ্যই @HiltAndroidApp অ্যানোটেশনযুক্ত একটি Application ক্লাস থাকতে হবে।

@HiltAndroidApp Hilt-এর কোড জেনারেশন চালু করে, যার মধ্যে আপনার অ্যাপ্লিকেশনের জন্য একটি বেস ক্লাস অন্তর্ভুক্ত থাকে, যা অ্যাপ্লিকেশন-স্তরের ডিপেন্ডেন্সি কন্টেইনার হিসেবে কাজ করে।

@HiltAndroidApp
class ExampleApplication : Application() { ... }

এই জেনারেট করা Hilt কম্পোনেন্টটি Application অবজেক্টের লাইফসাইকেলের সাথে সংযুক্ত থাকে এবং এটিকে ডিপেন্ডেন্সি সরবরাহ করে। এছাড়াও, এটি অ্যাপটির প্যারেন্ট কম্পোনেন্ট, যার অর্থ হলো অন্যান্য কম্পোনেন্টগুলো এর সরবরাহ করা ডিপেন্ডেন্সিগুলো অ্যাক্সেস করতে পারে।

অ্যান্ড্রয়েড ক্লাসে নির্ভরতা প্রবেশ করান

আপনার Application ক্লাসে Hilt সেট আপ করা হয়ে গেলে এবং একটি অ্যাপ্লিকেশন-স্তরের কম্পোনেন্ট উপলব্ধ থাকলে, Hilt সেইসব Android ক্লাসকে ডিপেন্ডেন্সি প্রদান করতে পারে যেগুলিতে @AndroidEntryPoint অ্যানোটেশন রয়েছে:

@AndroidEntryPoint
class ExampleActivity : ComponentActivity() { ... }

হিল্ট বর্তমানে নিম্নলিখিত অ্যান্ড্রয়েড ক্লাসগুলিকে সমর্থন করে:

  • Application ( @HiltAndroidApp ব্যবহার করে)
  • ViewModel ( @HiltViewModel ব্যবহার করে)
  • Activity
  • Service
  • BroadcastReceiver

Compose-এ, আপনাকে আলাদা আলাদা composable-কে অ্যানোটেট করার প্রয়োজন নেই। এর পরিবর্তে, আপনার রুট ComponentActivity @AndroidEntryPoint দিয়ে অ্যানোটেট করুন। এটি আপনার সম্পূর্ণ UI হায়ারার্কির জন্য একমাত্র DI এন্ট্রি পয়েন্ট হিসেবে কাজ করে, ফলে আপনি আপনার composable ফাংশনগুলোর ভেতর থেকে সরাসরি Hilt-ইনজেক্টেড ViewModel-গুলো অ্যাক্সেস করতে পারেন।

@AndroidEntryPoint আপনার প্রোজেক্টের প্রতিটি অ্যান্ড্রয়েড ক্লাসের জন্য একটি স্বতন্ত্র Hilt কম্পোনেন্ট তৈরি করে। কম্পোনেন্ট হায়ারার্কিতে বর্ণিত নিয়ম অনুযায়ী এই কম্পোনেন্টগুলো তাদের নিজ নিজ প্যারেন্ট ক্লাস থেকে ডিপেন্ডেন্সি গ্রহণ করতে পারে।

কোনো কম্পোনেন্ট থেকে ডিপেন্ডেন্সি পেতে, ফিল্ড ইনজেকশন সম্পাদন করার জন্য @Inject অ্যানোটেশনটি ব্যবহার করুন:

@AndroidEntryPoint
class ExampleActivity : ComponentActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

যেসব ক্লাসে হিল্ট ইনজেক্ট করে, সেগুলোর অন্যান্য বেস ক্লাসও থাকতে পারে যারা ইনজেকশন ব্যবহার করে। সেই ক্লাসগুলো অ্যাবস্ট্রাক্ট হলে সেগুলোতে @AndroidEntryPoint অ্যানোটেশনের প্রয়োজন হয় না।

একটি অ্যান্ড্রয়েড ক্লাস কোন লাইফসাইকেল কলব্যাকে ইনজেক্ট করা হয় সে সম্পর্কে আরও জানতে, কম্পোনেন্ট লাইফটাইমস দেখুন।

হিল্ট বাইন্ডিং সংজ্ঞায়িত করুন

ফিল্ড ইনজেকশন সম্পাদন করার জন্য, হিল্টকে জানতে হয় কীভাবে সংশ্লিষ্ট কম্পোনেন্ট থেকে প্রয়োজনীয় ডিপেন্ডেন্সিগুলোর ইনস্ট্যান্স সরবরাহ করতে হয়। একটি বাইন্ডিং-এ কোনো টাইপের ইনস্ট্যান্সকে ডিপেন্ডেন্সি হিসেবে সরবরাহ করার জন্য প্রয়োজনীয় তথ্য থাকে।

Hilt-কে বাইন্ডিং তথ্য দেওয়ার একটি উপায় হলো কনস্ট্রাক্টর ইনজেকশন । কোনো ক্লাসের ইনস্ট্যান্স কীভাবে সরবরাহ করতে হবে, তা Hilt-কে জানানোর জন্য সেই ক্লাসের কনস্ট্রাক্টরে @Inject অ্যানোটেশনটি ব্যবহার করুন:

class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

একটি ক্লাসের অ্যানোটেড কনস্ট্রাক্টরের প্যারামিটারগুলো হলো সেই ক্লাসের ডিপেন্ডেন্সিগুলো। উদাহরণস্বরূপ, AnalyticsAdapter একটি ডিপেন্ডেন্সি হলো AnalyticsService । সুতরাং, Hilt-কে অবশ্যই জানতে হবে কীভাবে AnalyticsService এর ইনস্ট্যান্স সরবরাহ করতে হয়।

হিল্ট মডিউল

কখনও কখনও একটি টাইপ কনস্ট্রাক্টর-ইনজেক্ট করা যায় না। এটি বিভিন্ন কারণে ঘটতে পারে। উদাহরণস্বরূপ, আপনি একটি ইন্টারফেস কনস্ট্রাক্টর-ইনজেক্ট করতে পারবেন না। এছাড়াও, আপনি এমন কোনো টাইপ কনস্ট্রাক্টর-ইনজেক্ট করতে পারবেন না যার মালিক আপনি নন, যেমন কোনো এক্সটার্নাল লাইব্রেরির ক্লাস। এইসব ক্ষেত্রে, আপনি Hilt মডিউল ব্যবহার করে Hilt-কে বাইন্ডিং তথ্য সরবরাহ করতে পারেন।

একটি Hilt মডিউল হলো এমন একটি ক্লাস যা @Module দিয়ে অ্যানোটেট করা থাকে। এটি Hilt-কে সেইসব টাইপের ইনস্ট্যান্স তৈরি করার নির্দেশনা দেয়, যেগুলো কনস্ট্রাক্টর ইনজেকশনের মাধ্যমে সরবরাহ করা যায় না, যেমন ইন্টারফেস বা থার্ড-পার্টি ক্লাস। এছাড়াও, প্রতিটি মডিউল কোন অ্যান্ড্রয়েড ক্লাসে ব্যবহৃত বা ইনস্টল করা হবে তা Hilt-কে জানানোর জন্য আপনাকে অবশ্যই প্রতিটি মডিউলকে @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
}

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)
  }
}

একই ধরণের জন্য একাধিক বাইন্ডিং প্রদান করুন

যেসব ক্ষেত্রে ডিপেন্ডেন্সি হিসেবে একই টাইপের বিভিন্ন ইমপ্লিমেন্টেশন Hilt-এর প্রয়োজন হয়, সেক্ষেত্রে আপনাকে অবশ্যই Hilt-কে একাধিক বাইন্ডিং সরবরাহ করতে হবে। আপনি কোয়ালিফায়ার ব্যবহার করে একই টাইপের জন্য একাধিক বাইন্ডিং সংজ্ঞায়িত করতে পারেন।

কোয়ালিফায়ার হলো এমন একটি অ্যানোটেশন যা কোনো একটি টাইপের একাধিক বাইন্ডিং সংজ্ঞায়িত থাকলে, সেই টাইপের একটি নির্দিষ্ট বাইন্ডিং শনাক্ত করতে ব্যবহার করা হয়।

উদাহরণটি বিবেচনা করুন। যদি আপনার AnalyticsService এ করা কলগুলো ইন্টারসেপ্ট করার প্রয়োজন হয়, তাহলে আপনি একটি ইন্টারসেপ্টরসহ OkHttpClient অবজেক্ট ব্যবহার করতে পারেন। অন্যান্য সার্ভিসের ক্ষেত্রে, আপনার হয়তো ভিন্ন উপায়ে কলগুলো ইন্টারসেপ্ট করার প্রয়োজন হতে পারে। সেক্ষেত্রে, OkHttpClient এর দুটি ভিন্ন ইমপ্লিমেন্টেশন কীভাবে সরবরাহ করতে হবে, তা আপনাকে Hilt-কে বলে দিতে হবে।

প্রথমে, @Binds বা @Provides মেথডগুলোকে টীকাযুক্ত করতে আপনি যে কোয়ালিফায়ারগুলো ব্যবহার করবেন, সেগুলো সংজ্ঞায়িত করুন:

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class 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()
  }
}

আপনি ফিল্ড বা প্যারামিটারকে সংশ্লিষ্ট কোয়ালিফায়ার দিয়ে টীকাযুক্ত করে আপনার প্রয়োজনীয় নির্দিষ্ট টাইপটি অন্তর্ভুক্ত করতে পারেন:

// 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: ComponentActivity() {

  @AuthInterceptorOkHttpClient
  @Inject lateinit var okHttpClient: OkHttpClient
}

একটি উত্তম অনুশীলন হিসেবে, যদি আপনি কোনো টাইপে কোয়ালিফায়ার যোগ করেন, তবে সেই ডিপেন্ডেন্সিটি সরবরাহ করার সম্ভাব্য সমস্ত উপায়েও কোয়ালিফায়ার যোগ করুন। বেস বা প্রচলিত ইমপ্লিমেন্টেশনে কোয়ালিফায়ার না রাখাটা ত্রুটিপ্রবণ এবং এর ফলে Hilt ভুল ডিপেন্ডেন্সি ইনজেক্ট করতে পারে।

হিল্টে পূর্বনির্ধারিত কোয়ালিফায়ার

Hilt কিছু পূর্বনির্ধারিত কোয়ালিফায়ার প্রদান করে। উদাহরণস্বরূপ, যেহেতু আপনার অ্যাপ্লিকেশন বা অ্যাক্টিভিটি উভয় জায়গা থেকেই Context ক্লাসের প্রয়োজন হতে পারে, তাই Hilt @ApplicationContext এবং @ActivityContext কোয়ালিফায়ারগুলো সরবরাহ করে।

ধরা যাক, উদাহরণে দেওয়া AnalyticsAdapter ক্লাসটির অ্যাক্টিভিটির কনটেক্সট প্রয়োজন। নিচের কোডটিতে দেখানো হয়েছে কিভাবে AnalyticsAdapter কে অ্যাক্টিভিটি কনটেক্সট সরবরাহ করতে হয়:

class AnalyticsAdapter @Inject constructor(
    @ActivityContext private val context: Context,
    private val service: AnalyticsService
) { ... }

Hilt-এ উপলব্ধ অন্যান্য পূর্বনির্ধারিত বাইন্ডিংগুলির জন্য, কম্পোনেন্ট ডিফল্ট বাইন্ডিং দেখুন।

অ্যান্ড্রয়েড ক্লাসের জন্য তৈরি উপাদান

প্রতিটি অ্যান্ড্রয়েড ক্লাসের জন্য, যেখানে আপনি ফিল্ড ইনজেকশন করতে পারেন, একটি সংশ্লিষ্ট Hilt কম্পোনেন্ট থাকে, যা আপনি @InstallIn অ্যানোটেশনে উল্লেখ করতে পারেন। প্রতিটি Hilt কম্পোনেন্ট তার বাইন্ডিংগুলোকে সংশ্লিষ্ট অ্যান্ড্রয়েড ক্লাসে ইনজেক্ট করার জন্য দায়ী থাকে।

পূর্ববর্তী উদাহরণগুলিতে হিল্ট মডিউলে ActivityComponent ব্যবহার দেখানো হয়েছে।

হিল্ট নিম্নলিখিত উপাদানগুলি সরবরাহ করে:

হিল্ট উপাদান ইনজেক্টরের জন্য
SingletonComponent Application
ActivityRetainedComponent প্রযোজ্য নয়
ViewModelComponent ViewModel
ActivityComponent Activity
ServiceComponent Service

উপাদানের জীবনকাল

Hilt স্বয়ংক্রিয়ভাবে সংশ্লিষ্ট অ্যান্ড্রয়েড ক্লাসগুলির জীবনচক্র অনুসরণ করে জেনারেট করা কম্পোনেন্ট ক্লাসগুলির ইনস্ট্যান্স তৈরি ও ধ্বংস করে।

উত্পাদিত উপাদান তৈরি করা হয়েছে ধ্বংস করা হয়েছে
SingletonComponent Application#onCreate() Application ধ্বংস হয়ে গেছে
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent ViewModel তৈরি করা হয়েছে ViewModel ধ্বংস করা হয়েছে
ActivityComponent Activity#onCreate() Activity#onDestroy()
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
Service ServiceComponent @ServiceScoped

উদাহরণে, যদি আপনি @ActivityScoped ব্যবহার করে AnalyticsAdapter ActivityComponent এর মধ্যে সীমাবদ্ধ করেন, তাহলে Hilt সংশ্লিষ্ট অ্যাক্টিভিটির পুরো জীবনকাল জুড়ে AnalyticsAdapter এর একই ইনস্ট্যান্স সরবরাহ করে:

@ActivityScoped
class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

ধরা যাক, AnalyticsService এমন একটি অভ্যন্তরীণ অবস্থা আছে যার জন্য প্রতিবার একই ইনস্ট্যান্স ব্যবহার করা প্রয়োজন—শুধু ExampleActivity তেই নয়, অ্যাপের যেকোনো জায়গায়। এই ক্ষেত্রে, AnalyticsService SingletonComponent এর স্কোপের মধ্যে রাখা উপযুক্ত। এর ফলে, যখনই কম্পোনেন্টটির AnalyticsService এর একটি ইনস্ট্যান্স সরবরাহ করার প্রয়োজন হয়, এটি প্রতিবার একই ইনস্ট্যান্স সরবরাহ করে।

নিম্নলিখিত উদাহরণটি দেখায় কিভাবে একটি Hilt মডিউলে কোনো কম্পোনেন্টের জন্য একটি বাইন্ডিং-এর স্কোপ নির্ধারণ করতে হয়। একটি বাইন্ডিং-এর স্কোপ অবশ্যই সেই কম্পোনেন্টের স্কোপের সাথে মিলতে হবে যেখানে এটি ইনস্টল করা হয়েছে, তাই এই উদাহরণে আপনাকে ActivityComponent এর পরিবর্তে SingletonComponentAnalyticsService ইনস্টল করতে হবে:

// 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)
  }
}

Hilt কম্পোনেন্ট স্কোপ সম্পর্কে আরও জানতে, Android এবং Hilt-এ স্কোপিং দেখুন।

উপাদানের শ্রেণিবিন্যাস

কোনো কম্পোনেন্টে একটি মডিউল ইনস্টল করলে, সেই কম্পোনেন্টের অথবা কম্পোনেন্ট হায়ারার্কিতে এর নিচের যেকোনো চাইল্ড কম্পোনেন্টের অন্যান্য বাইন্ডিংয়ের ডিপেন্ডেন্সি হিসেবে এর বাইন্ডিংগুলো অ্যাক্সেস করা যায়।

ActivityComponent আছে ActivityRetainedComponent-এর অধীনে। ViewModelComponent আছে ActivityRetainedComponent-এর অধীনে। ActivityRetainedComponent এবং ServiceComponent আছে SingletonComponent-এর অধীনে।
চিত্র ১. হিল্ট কর্তৃক উৎপাদিত উপাদানগুলোর ক্রমবিন্যাস।

কম্পোনেন্ট ডিফল্ট বাইন্ডিং

প্রতিটি Hilt কম্পোনেন্টের সাথে এক সেট ডিফল্ট বাইন্ডিং থাকে, যা Hilt আপনার নিজস্ব কাস্টম বাইন্ডিং-এ ডিপেন্ডেন্সি হিসেবে ইনজেক্ট করতে পারে। মনে রাখবেন যে, এই বাইন্ডিংগুলো সাধারণ অ্যাক্টিভিটি টাইপের সাথে সম্পর্কিত, কোনো নির্দিষ্ট সাবক্লাসের সাথে নয়। এর কারণ হলো, Hilt সমস্ত অ্যাক্টিভিটি ইনজেক্ট করার জন্য একটিমাত্র অ্যাক্টিভিটি কম্পোনেন্ট ডেফিনিশন ব্যবহার করে। প্রতিটি অ্যাক্টিভিটির জন্য এই কম্পোনেন্টের একটি ভিন্ন ইনস্ট্যান্স থাকে।

অ্যান্ড্রয়েড উপাদান ডিফল্ট বাইন্ডিং
SingletonComponent Application
ActivityRetainedComponent Application
ViewModelComponent SavedStateHandle
ActivityComponent Application , Activity
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 { ... }

@ActivityContext ব্যবহার করেও অ্যাক্টিভিটি কনটেক্সট বাইন্ডিং করা যায়। উদাহরণস্বরূপ:

class AnalyticsAdapter @Inject constructor(
  @ActivityContext context: Context
) { ... }

// The Activity binding is available without qualifiers.
class AnalyticsAdapter @Inject constructor(
  activity: ComponentActivity
) { ... }

হিল্ট দ্বারা সমর্থিত নয় এমন ক্লাসগুলিতে নির্ভরতা ইনজেক্ট করুন।

Compose-এ প্রচলিত রীতি হলো কনস্ট্রাক্টর ইনজেকশন ব্যবহার করে একটি @HiltViewModel এ ডিপেন্ডেন্সি ইনজেক্ট করা এবং তারপর ViewModel অ্যাক্সেস করার জন্য আপনার কম্পোজেবলের ভিতরে hiltViewModel() ব্যবহার করা। যদিও Hilt সবচেয়ে প্রচলিত অ্যান্ড্রয়েড ক্লাসগুলোকে সাপোর্ট করে, তবুও আপনি এমন কিছু অসমর্থিত ক্লাসের সম্মুখীন হতে পারেন যেখানে আপনার ফিল্ড ইনজেকশন করার প্রয়োজন হবে।

সেই ক্ষেত্রে, আপনি @EntryPoint অ্যানোটেশন ব্যবহার করে একটি এন্ট্রি পয়েন্ট তৈরি করতে পারেন। এন্ট্রি পয়েন্ট হলো Hilt দ্বারা পরিচালিত কোড এবং অপরিচালিত কোডের মধ্যকার সীমানা। এটি সেই বিন্দু যেখানে কোড সর্বপ্রথম Hilt দ্বারা পরিচালিত অবজেক্টের গ্রাফে প্রবেশ করে। এন্ট্রি পয়েন্টগুলো Hilt-কে এমন কোড ব্যবহার করার সুযোগ দেয় যা সে নিজে পরিচালনা করে না, এবং এর মাধ্যমে ডিপেন্ডেন্সি গ্রাফের মধ্যে ডিপেন্ডেন্সি সরবরাহ করে।

উদাহরণস্বরূপ, Hilt সরাসরি কন্টেন্ট প্রোভাইডার সমর্থন করে না। যদি আপনি চান যে কোনো কন্টেন্ট প্রোভাইডার Hilt ব্যবহার করে কিছু ডিপেন্ডেন্সি গ্রহণ করুক, তাহলে আপনার কাঙ্ক্ষিত প্রতিটি বাইন্ডিং টাইপের জন্য @EntryPoint দিয়ে অ্যানোটেটেড একটি ইন্টারফেস সংজ্ঞায়িত করতে হবে এবং কোয়ালিফায়ার অন্তর্ভুক্ত করতে হবে। তারপর, এন্ট্রি পয়েন্টটি কোন কম্পোনেন্টে ইনস্টল করতে হবে তা নির্দিষ্ট করার জন্য @InstallIn যোগ করুন, যেমনটি নিচে দেখানো হলো:

class ExampleContentProvider : ContentProvider() {

  @EntryPoint
  @InstallIn(SingletonComponent::class)
  interface ExampleContentProviderEntryPoint {
    fun 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()
    ...
  }
}

এই উদাহরণে, এন্ট্রি পয়েন্টটি পুনরুদ্ধার করার জন্য আপনাকে অবশ্যই ApplicationContext ব্যবহার করতে হবে, কারণ এন্ট্রি পয়েন্টটি SingletonComponent এ ইনস্টল করা আছে। আপনি যে বাইন্ডিংটি পুনরুদ্ধার করতে চাইছেন তা যদি ActivityComponent এ থাকতো, তাহলে আপনি এর পরিবর্তে ActivityContext ব্যবহার করতেন।

তলোয়ার এবং ছোরা

অ্যান্ড্রয়েডে ডিপেন্ডেন্সি ইনজেকশনের জন্য হিল্ট হলো আনুষ্ঠানিকভাবে প্রস্তাবিত লাইব্রেরি। এটি আপনার অ্যাপ্লিকেশনে ডিপেন্ডেন্সি ইনজেকশন বাস্তবায়নের জন্য একটি মানসম্মত, সুনির্দিষ্ট এবং কার্যকর উপায় প্রদান করে, যা বিশেষভাবে জেটপ্যাক কম্পোজ এবং সিঙ্গেল-অ্যাক্টিভিটি আর্কিটেকচারের জন্য অপ্টিমাইজ করা হয়েছে।

হিল্টের লক্ষ্যগুলো নিম্নরূপ:

  • অ্যাপগুলোর মধ্যে সেটআপ, পঠনযোগ্যতা এবং কোড শেয়ারিং সহজ করার জন্য কম্পোনেন্ট ও স্কোপের একটি স্ট্যান্ডার্ড সেট তৈরি করা।
  • টেস্টিং, ডিবাগ বা রিলিজের মতো বিভিন্ন বিল্ড টাইপের জন্য আলাদা আলাদা বাইন্ডিং সহজে সরবরাহ করার একটি উপায় প্রদান করা।

যেহেতু অ্যান্ড্রয়েড অপারেটিং সিস্টেম তার নিজস্ব অনেক ফ্রেমওয়ার্ক ক্লাস ইনস্ট্যানশিয়েট করে, তাই একটি অ্যান্ড্রয়েড অ্যাপে ড্যাগার ব্যবহার করতে গেলে আপনাকে প্রচুর পরিমাণে বয়লারপ্লেট কোড লিখতে হয়। হিল্ট একটি অ্যান্ড্রয়েড অ্যাপ্লিকেশনে ড্যাগার ব্যবহারের সাথে জড়িত বয়লারপ্লেট কোড কমিয়ে দেয়। হিল্ট স্বয়ংক্রিয়ভাবে নিম্নলিখিত বিষয়গুলো তৈরি করে এবং সরবরাহ করে:

  • ড্যাগারের সাথে অ্যান্ড্রয়েড ফ্রেমওয়ার্ক ক্লাসগুলিকে একীভূত করার জন্য এমন সব উপাদান , যা অন্যথায় আপনাকে হাতে তৈরি করতে হতো।
  • হিল্ট দ্বারা স্বয়ংক্রিয়ভাবে তৈরি কম্পোনেন্টগুলোর সাথে ব্যবহার করার জন্য স্কোপ অ্যানোটেশন
  • Application বা Activity মতো অ্যান্ড্রয়েড ক্লাসগুলোকে উপস্থাপন করার জন্য পূর্বনির্ধারিত বাইন্ডিং
  • @ApplicationContext এবং @ActivityContext উপস্থাপন করার জন্য পূর্বনির্ধারিত কোয়ালিফায়ার

একই কোডবেসে ড্যাগার এবং হিল্ট কোড একসাথে থাকতে পারে। তবে, বেশিরভাগ ক্ষেত্রে অ্যান্ড্রয়েডে ড্যাগারের সমস্ত ব্যবহার পরিচালনার জন্য হিল্ট ব্যবহার করাই শ্রেয়। ড্যাগার ব্যবহার করে এমন কোনো প্রজেক্ট হিল্টে মাইগ্রেট করতে, মাইগ্রেশন গাইডটি দেখুন।

অতিরিক্ত সম্পদ

হিল্ট সম্পর্কে আরও জানতে, নিম্নলিখিত অতিরিক্ত তথ্যসূত্রগুলো দেখুন।

নমুনা

ব্লগ

বিষয়বস্তু দেখুন