Hilt-এর সাথে ডিপেন্ডেন্সি ইনজেকশন

Hilt হল Android-এর জন্য একটি ডিপেন্ডেন্সি ইনজেকশন লাইব্রেরি যা আপনার প্রোজেক্টে ম্যানুয়াল ডিপেন্ডেন্সি ইনজেকশন করার বয়লারপ্লেট কমায়। ম্যানুয়াল ডিপেন্ডেন্সি ইনজেকশন করতে হলে আপনাকে প্রতিটি ক্লাস ও তার ডিপেন্ডেন্সি ম্যানুয়ালি তৈরি করতে হবে এবং ডিপেন্ডেন্সি আবার ব্যবহার ও ম্যানেজ করার জন্য কন্টেনার ব্যবহার করতে হবে।

Hilt আপনার প্রোজেক্টের প্রতিটি Android ক্লাসের জন্য কন্টেনার প্রদান করে এবং তাদের লাইফসাইকেল অটোমেটিক ম্যানেজ করে আপনার অ্যাপ্লিকেশনে DI ব্যবহার করার একটি স্ট্যান্ডার্ড উপায় প্রদান করে। Hilt জনপ্রিয় DI লাইব্রেরি Dagger-এর উপর তৈরি করা হয়েছে যাতে কম্পাইল-টাইম নির্ভুলতা, রানটাইম পারফর্ম্যান্স, স্কেলেবিলিটি ও Android Studio সহায়তা যা Dagger প্রদান করে, তার সুবিধা পাওয়া যায়। আরও তথ্যের জন্য, Hilt এবং Dagger দেখুন।

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

ডিপেন্ডেন্সি যোগ করা হচ্ছে

প্রথমে, আপনার প্রোজেক্টের রুট build.gradle ফাইলে hilt-android-gradle-plugin প্লাগ-ইন যোগ করুন:

Kotlin

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
}

তারপর, Gradle প্লাগ-ইন প্রয়োগ করুন এবং আপনার app/build.gradle ফাইলে এইসব ডিপেন্ডেন্সি যোগ করুন:

Kotlin

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

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

Kotlin

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

গ্ৰুভি

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

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

Hilt ব্যবহার করে এমন সব অ্যাপে অবশ্যই একটি Application ক্লাস থাকতে হবে যা @HiltAndroidApp-এর সাথে অ্যানোটেট করা আছে।

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

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

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

Android ক্লাসে ডিপেন্ডেন্সি ইনজেক্ট করুন

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

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

Hilt বর্তমানে নিম্নলিখিত Android ক্লাস সাপোর্ট করে:

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

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

@AndroidEntryPoint আপনার প্রোজেক্টের প্রতিটি Android ক্লাসের জন্য একটি স্বতন্ত্র Hilt কম্পোনেন্ট তৈরি করে। কম্পোনেন্ট হায়ারার্কি-তে যেমন বর্ণনা করা হয়েছে, এইসব কম্পোনেন্ট তাদের নিজ নিজ পেরেন্ট ক্লাস থেকে ডিপেন্ডেন্সি পেতে পারে।

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

@AndroidEntryPoint
class ExampleActivity : ComponentActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

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

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

Hilt বাইন্ডিংয়ের সংজ্ঞা দাও

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

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

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

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

Hilt মডিউল

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

Hilt মডিউল হল একটি ক্লাস যা @Module-এর সাথে অ্যানোটেট করা থাকে। এটি Hilt-কে এমন ধরনের ইনস্ট্যান্স তৈরি করার নির্দেশাবলী প্রদান করে যা কন্সট্রাক্টর ইনজেকশনের মাধ্যমে প্রদান করা যায় না, যেমন ইন্টারফেস বা থার্ড-পার্টি ক্লাস। এছাড়াও, প্রতিটি মডিউল কোন Android ক্লাসে ব্যবহার বা ইনস্টল করা হবে তা Hilt-কে জানাতে, আপনাকে প্রতিটি মডিউল @InstallIn দিয়ে অ্যানোটেট করতে হবে।

Hilt মডিউলে আপনার দেওয়া ডিপেন্ডেন্সি, Android ক্লাসের সাথে যুক্ত সব জেনারেট করা কম্পোনেন্টে উপলভ্য থাকে, যেখানে আপনি Hilt মডিউল ইনস্টল করেন।

@Binds-এর সাথে ইন্টারফেস ইনস্ট্যান্স ইনজেক্ট করুন

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

@Binds অ্যানোটেশন Hilt-কে বলে দেয় যে ইন্টারফেসের ইনস্ট্যান্স প্রদান করার প্রয়োজন হলে কোন ইমপ্লিমেন্টেশন ব্যবহার করতে হবে।

অ্যানোটেট করা ফাংশন Hilt-কে নিম্নলিখিত তথ্য প্রদান করে:

  • ফাংশন রিটার্ন টাইপ 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, বা রুম ডেটাবেস-এর মতো ক্লাস) থেকে আসে অথবা বিল্ডার প্যাটার্ন ব্যবহার করে ইনস্ট্যান্স তৈরি করতে হয়।

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

অ্যানোটেট করা ফাংশন Hilt-কে নিম্নলিখিত তথ্য প্রদান করে:

  • ফাংশন রিটার্ন টাইপ Hilt-কে বলে যে ফাংশনটি কোন ধরনের ইনস্ট্যান্স প্রদান করে।
  • ফাংশন প্যারামিটার Hilt-কে সংশ্লিষ্ট ধরনের ডিপেন্ডেন্সি সম্পর্কে জানায়।
  • ফাংশন বডি Hilt-কে বলে যে কীভাবে সংশ্লিষ্ট ধরনের ইনস্ট্যান্স প্রদান করতে হবে। 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-এ আগে থেকে নির্ধারিত কোয়ালিফায়ার

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

ধরা যাক, উদাহরণে দেখানো AnalyticsAdapter ক্লাসের জন্য অ্যাক্টিভিটির প্রসঙ্গ প্রয়োজন। নিম্নলিখিত কোডটি দেখায় যে কীভাবে AnalyticsAdapter-এ অ্যাক্টিভিটি কন্টেক্সট প্রদান করতে হয়:

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

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

Android ক্লাসের জন্য জেনারেট করা কম্পোনেন্ট

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

আগের উদাহরণে Hilt মডিউলে ActivityComponent-এর ব্যবহার দেখানো হয়েছে।

Hilt-এ নিম্নলিখিত কম্পোনেন্ট থাকে:

Hilt কম্পোনেন্ট এর জন্য ইনজেক্টর
SingletonComponent Application
ActivityRetainedComponent N/A
ViewModelComponent ViewModel
ActivityComponent Activity
ServiceComponent Service

কম্পোনেন্টের লাইফটাইম

Hilt অটোমেটিক জেনারেট করা কম্পোনেন্ট ক্লাসের ইনস্ট্যান্স তৈরি ও ধ্বংস করে সংশ্লিষ্ট Android ক্লাসের লাইফসাইকেল অনুসরণ করে।

জেনারেট করা কম্পোনেন্ট তৈরির তারিখ এই সময়ে ধ্বংস করা হয়েছে
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 শুধুমাত্র কম্পোনেন্টের প্রতিটি ইনস্ট্যান্সের জন্য একবার স্কোপড বাইন্ডিং তৈরি করে, যেটির সাথে বাইন্ডিং স্কোপ করা হয় এবং সেই বাইন্ডিংয়ের জন্য সমস্ত অনুরোধ একই ইনস্ট্যান্স শেয়ার করে।

নিচের সারণীতে প্রতিটি জেনারেট করা কম্পোনেন্টের জন্য স্কোপ অ্যানোটেশন তালিকাভুক্ত করা হয়েছে:

Android ক্লাস জেনারেট করা কম্পোনেন্ট সুযোগ
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Service ServiceComponent @ServiceScoped

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

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

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

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

// 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 আপনার কাস্টম বাইন্ডিংয়ে ডিপেন্ডেন্সি হিসেবে ইনজেক্ট করতে পারে। মনে রাখবেন যে এই বাইন্ডিংগুলি সাধারণ অ্যাক্টিভিটির ধরনের সাথে সম্পর্কিত এবং কোনও নির্দিষ্ট সাবক্লাসের সাথে নয়। কারণ, Hilt সব অ্যাক্টিভিটি ইনজেক্ট করার জন্য একটি অ্যাক্টিভিটি কম্পোনেন্ট সংজ্ঞা ব্যবহার করে। প্রতিটি অ্যাক্টিভিটিতে এই কম্পোনেন্টের আলাদা ইনস্ট্যান্স থাকে।

Android কম্পোনেন্ট ডিফল্ট বাইন্ডিং
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
) { ... }

Hilt-এ কাজ করে না এমন ক্লাসে ডিপেন্ডেন্সি ইনজেক্ট করুন

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

এইসব ক্ষেত্রে, আপনি @EntryPoint অ্যানোটেশন ব্যবহার করে এন্ট্রি পয়েন্ট তৈরি করতে পারবেন। এন্ট্রি পয়েন্ট হল Hilt দ্বারা ম্যানেজ করা কোড এবং ম্যানেজ না করা কোডের মধ্যে সীমানা। এটি হল সেই পয়েন্ট যেখানে কোডটি প্রথমে 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 ব্যবহার করবেন।

Hilt এবং Dagger

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

Hilt-এর লক্ষ্যগুলি হল:

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

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

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

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

অতিরিক্ত রিসোর্স

Hilt সম্পর্কে আরও জানতে, নিম্নলিখিত অতিরিক্ত রিসোর্স দেখুন।

নমুনা

ব্লগ

কন্টেন্ট দেখে