কম্পিউটার টিউটোরিয়াল

Dagger 2 দিয়ে Android-এ Dependency Injection মাস্টার করুন: ধাপে ধাপে গাইড

Kriptofolio app series - Part 4

Dependency injection আপনার কোডকে উল্লেখযোগ্যভাবে উন্নত করবে। এটি আপনার কোডকে আরও মডুলার, ফ্লেক্সিবল এবং টেস্টেবল করে তোলে। আসলে এর নাম শোনা'idea'-এর চেয়ে জটিল লাগে।

সিরিজের এই পার্টে আমরা dependency injection শিখব। তারপর এটিকে "Kriptofolio" (পূর্বে "My Crypto Coins") অ্যাপে ইমপ্লিমেন্ট করব। আমরা Dagger 2 ব্যবহার করব। Dagger 2 Android-র জন্য সবচেয়ে জনপ্রিয় ওপেন-सোর্স dependency injection ফ্রেমওয়ার্ক। আধুনিক অ্যাপ বানানোর জন্য এটি একটি মূল্যবান স্কিল, যদিও লার্নিং কভার কঠিন।

Series content

  • Introduction: A roadmap to build a modern Android app in 2018–2019
  • Part 1: An introduction to the SOLID principles
  • Part 2: How to start building your Android app: creating Mockups, UI, and XML layouts
  • Part 3: All about that Architecture: exploring different architecture patterns and how to use them in your app
  • Part 4: How to implement Dependency Injection in your app with Dagger 2 (you’re here)
  • Part 5: Handle RESTful Web Services using Retrofit, OkHttp, Gson, Glide and Coroutines

Dependency Injection কী?

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

class MyAppClass() { private val library: MyLibrary = MyLibrary(true) ... } class MyLibrary(private val useSpecialFeature: Boolean) { ... }

উপরের উদাহরণ থেকে দেখা যাচ্ছে আপনার ক্লাস MyAppClass সরাসরি MyLibrary ক্লাসের কনক্রিট কনফিগারেশন ও ইমপ্লিমেন্টেশনের ওপর নির্ভরশীল। যদি একদিন আপনি থার্ড-পার্টি লাইব্রেরি ব্যবহার করতে চান? যদি আপনি অন্য একটি ক্লাসে একই লাইব্রেরি কনফিগারেশন ব্যবহার করতে চান? প্রতিবার আপনাকে কোডে খুঁজে বের করে সঠিক জায়গা পরিবর্তন করতে হবে। এটা শুধু কিছু উদাহরণ।

আইডিয়া হলো অ্যাপ্লিকেশনের কম্পোনেন্টের মধ্যে এই টাইট কাপ্লিং (tight coupling) আপনার ডেভেলপমেন্ট কাজকে কঠিন করে দেবে যতক্ষণ আপনার প্রজেক্ট বড় হবে। যেকোনো সমস্যা এড়াতে, dependency injection ব্যবহার করুন কাপ্লিং লুজ (loosening) করার জন্য।

class MyAppClass(private val library: MyLibrary) { ... } class MyLibrary(private val useSpecialFeature: Boolean) { ... }

এটাই হলো, এটি একটি খুব প্রাথমিক dependency injection উদাহরণ। আপনার ক্লাস MyAppClass-এর ভিতরে নতুন MyLibrary ক্লাস অবজেক্ট তৈরি ও কনফিগার করার বদলে, আপনি কনস্ট্রাক্টরে এটিকে পাস বা inject করলেন। তাই MyAppClass MyLibrary-এর জন্য সম্পূর্ণ দায়বদ্ধতা থেকে মুক্ত।

Dagger 2 কী?

Dagger একটি ফুলি স্ট্যাটিক, কম্পাইল-টাইম, ওপেন-सোর্স dependency injection ফ্রেমওয়ার্ক Java এবং Android উভয়ের জন্য। এই আর্টিকেলে আমি এর সেকেন্ড ভার্সনের কথা বলব যা Google মেইনটেইন করে। Square এরলি ভার্সন তৈরি করেছিল।

Dagger 2 কে আজকের তারিখে সবচেয়ে কার্যকর dependency injection ফ্রেমওয়ার্ক হিসেবে বিবেচনা করা হয়। আসলে যদি আপনি Dagger 1, Dagger 2 এবং Dagger 2.10 তুলনা করেন, তবে প্রতিটি ইমপ্লিমেন্টেশন আলাদা পাবেন। প্রতিবার নতুন করে শিখতে হয় কারণ লেখকদের 의해 উল্লেখযোগ্য পরিবর্তন হয়েছে। এই আর্টিকেল লিখতে সময় আমি Dagger 2.16 ভার্সন ব্যবহার করছি এবং শুধু এটিতে ফোকাস করব।

আপনি যেহেতু dependency injection বুঝতে পারলেন, আমাদের ক্লাসগুলো dependency তৈরি বা ধারণ করবেন না। বরং সেগুলো বাইর থেকে পাবে। Dagger 2 ব্যবহার করলে, এই ফ্রেমওয়ার্ক সব প্রয়োজনীয় dependency প্রদান করবে।

এটি খুব সারাবوইলারপ্লেট কোড জেনারেট করে আমাদের জন্য। জেনারেটেড কোডটি ফুলি ট্রেসেবল এবং যেকোনো ইউজার হাতের লিখা কোডের মতো মিমিক করে। Dagger 2 Java-এ লিখা এবং এর অ্যানোতেশন প্রসেসর দ্বারা জেনারেটেড কোডও Java কোড হবে।

তবে এটি Kotlin-এ কোনো সমস্যা বা মডিফিকেশন ছাড়া কাজ করে। মনে রাখবেন Kotlin Java-র সাথে ফুলি ইন্টারঅপারেবল। অন্য ফ্রেমওয়ার্কের সাথে তুলনা করলে, Dagger 2 কম ডাইনামিক। এটি কম্পাইল টাইমে কাজ করে রান-টাইম রিফ্লেকশনের বদলে। কোনো রিফ্লেকশন ইউজ নেই। এই সব অর্থে এই ফ্রেমওয়ার্ক সেটআপ ও শিখতে কঠিন হবে। কিন্তু কম্পাইল-টাইম সেফটি সহ পারফরম্যান্স বুট দেবে।

টুল ছাড়া ম্যানুয়াল Dependency Injection

আপনি পূর্বের পার্টের My Crypto Coins অ্যাপ সোর্স কোডে লক্ষ্য করেছেন থাকতে পারেন যে কোনো dependency injection টুল ছাড়া অবজেক্ট inject করার জন্য একটি কোড পিস আছে। এটি ঠিক কাজ করে, এবং এই সমাধান এমন ছোট অ্যাপের জন্য যথেষ্ট ভালো। utilities package-এ নজর দিন:

/** * Static methods used to inject classes needed for various Activities and Fragments. */ object InjectorUtils { private fun getCryptocurrencyRepository(context: Context): CryptocurrencyRepository { return CryptocurrencyRepository.getInstance( AppDatabase.getInstance(context).cryptocurrencyDao()) } fun provideMainViewModelFactory( application: Application ): MainViewModelFactory { val repository = getCryptocurrencyRepository(application) return MainViewModelFactory(application, repository) } fun provideAddSearchViewModelFactory( context: Context ): AddSearchViewModelFactory { val repository = getCryptocurrencyRepository(context) return AddSearchViewModelFactory(repository) } }

যে দেখতে পাচ্ছেন এই ক্লাস সारा কাজ করবে। এটি ViewModel factory তৈরি করবে অ্যাক্টিভিটি বা ফ্র্যাগমেন্টের জন্য যেগুলো দরকার।

/** * Factory for creating a [MainViewModel] with a constructor that takes a * [CryptocurrencyRepository]. */ class MainViewModelFactory(private val application: Application, private val repository: CryptocurrencyRepository) : ViewModelProvider.NewInstanceFactory() { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel?> create(modelClass: Class<T>): T { return MainViewModel(application, repository) as T } }

তারপর InjectorUtils ক্লাস ব্যবহার করুন যেখানে স্পেসিফিক ViewModel factory দরকার:

/** * A placeholder fragment containing a simple view. */ class MainListFragment : Fragment() { ... private lateinit var viewModel: MainViewModel ... override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) setupList() ... } ... private fun subscribeUi(activity: FragmentActivity) { // Dagger ব্যবহার করার আগে আমরা কীভাবে কোড inject করতাম। val factory = InjectorUtils.provideMainViewModelFactory(activity.application) // ViewModelProviders থেকে ViewModel পাওয়া, প্যারেন্ট অ্যাক্টিভিটি ব্যবহার করে LifecycleOwner হিসেবে। viewModel = ViewModelProviders.of(activity, factory).get(MainViewModel::class.java) ... } }

যে দেখতে পাচ্ছেন আমাদের MainListFragment ক্লাস CryptocurrencyRepository বা AppDatabase সম্পর্কে জানতেই না। এটি InjectorUtils ক্লাস থেকে সফলভাবে কনস্ট্রাক্ট করা factory পায়। আসলে এটি একটা সিম্পল উপায়। আমরা এটিকে মুছে ফেলব এবং শিখব Dagger 2 টুল সেটআপ করুন advanced dependency injection-এর জন্য। যদি এই অ্যাপ ফাংশন্যালিটি ও কোড-এ এক্সপ্যান্ড করত, তবে আমার কোনো সন্দেহ নেই যে প্রফেশনাল dependency injection ফ্রেমওয়ার্কের বেনিফিট দ্রুত দেখা যেত ম্যানুয়াল সমাধানের চেয়ে।

তাহলে InjectorUtils ক্লাস ডিলিট করুন এখন এবং শিখুন My Crypto Coins অ্যাপ সোর্স কোডে Dagger 2 কীভাবে সেটআপ করবেন।

Kotlin-এ MVVM-এর জন্য Dependency Injection

ViewModels, Activities এবং Fragments-এ Dagger 2 সেটআপ করার পদ্ধতি

এখন আমরা My Crypto Coins অ্যাপ প্রজেক্টে Dagger 2 ধাপে ধাপে সেটআপ করব।

শুরুতে, Kotlin-এর নিজস্ব Annotation Processing Tool (kapt) ইনেবল করুন। তারপর স্পেশাল Dagger 2 ডিপেন্ডেন্সি যোগ করুন।

আপনার gradle ফাইলে এই লাইনগুলো যোগ করে করতে পারেন:

apply plugin: 'kotlin-kapt' // Annotation processing-এর জন্য ... implementation "com.google.dagger:dagger:$versions.dagger" implementation "com.google.dagger:dagger-android:$versions.dagger" implementation "com.google.dagger:dagger-android-support:$versions.dagger" kapt "com.google.dagger:dagger-compiler:$versions.dagger" kapt "com.google.dagger:dagger-android-processor:$versions.dagger"
Module: app

Kapt প্লাগইন কম্পাইলারকে স্টাব ক্লাস জেনারেট করতে সক্ষম করবে Java এবং Kotlin-এর ইন্টারঅপারেবিলিটির জন্য। সুবিধার্থে আমরা কনক্রিট Dagger 2 ভার্সন আলাদা gradle ফাইলে ডিফাইন করব, যেমন আমরা সব ডিপেন্ডেন্সির জন্য করি।

def versions = [:] versions.dagger = "2.16" ext.versions = versions

লেটেস্ট ভার্সন পেতে Dagger 2-এর অফিসিয়াল GitHub রিপোজিটরির রিলিজ চেক করুন।

এখন, আপনার অ্যাপ্লিকেশন App ক্লাস তৈরি করুন।

এটি বাদ দিন যদি আপনার কাছে ইতিমধ্যে এই ক্লাস সেট থাকে। আপনি করার পর, এটিকে কিছুক্ষণ অপরিবর্তিত রেখে দিন, কিন্তু পরে ফিরে আসব।

class App : Application() { override fun onCreate() { super.onCreate() } }

My Crypto Coins অ্যাপের জন্য, আমরা আগে থেকেই অ্যাপ্লিকেশন ক্লাস তৈরি করেছি।

পরবর্তী, ম্যানিফেস্ট ফাইল আপডেট করুন আপনার App ক্লাস ইনেবল করার জন্য।

বাদ দিন যদি আগে থেকেই করা থাকে।

<manifest xmlns:android="https://schemas.android.com/apk/res/android" package="com.baruckis.mycryptocoins"> <application android:name=".App" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" ...

My Crypto Coins অ্যাপের জন্য, আমরা App ক্লাস ম্যানিফেস্টে আগে থেকেই সেট করেছি।

এখন dependencyinjection নামে একটি নতুন প্যাকেজ তৈরি করুন।

এখানে Dagger ইমপ্লিমেন্টেশন সংক্রান্ত সব ফাইল রাখব।

AppModule ক্লাস মডিউল তৈরি করুন যা পুরো অ্যাপ্লিকেশনের dependency প্রদান করবে।

/** * AppModule অ্যাপ-ওয়াইড dependency প্রদান করবে অ্যাপ্লিকেশনের একটি অংশের জন্য। * এটি অ্যাপ্লিকেশনের سرাসরি ব্যবহৃত অবজেক্ট ইনিশিয়ালাইজ করবে, যেমন Room database, Retrofit, Shared Preference, ইত্যাদি। */ @Module(includes = [ViewModelsModule::class]) class AppModule() { @Singleton // এই অ্যানোতেশন Dagger কম্পাইলারকে বলে যে ইনস্ট্যান্স পুরো অ্যাপ্লিকেশন লাইফসাইকেলে শুধু একবার তৈরি হবে। @Provides // এই অ্যানোতেশন Dagger কম্পাইলারকে বলে যে এই মেথডটি Context রিটার্ন টাইপের কনস্ট্রাক্টর। fun provideContext(app: App): Context = app // provide প্রিফিক্স ব্যবহার করা সাধারণ কনভেনশন কিন্তু বাধ্যতামূলক নয়। @Singleton @Provides fun provideCryptocurrencyRepository(context: Context): CryptocurrencyRepository { return CryptocurrencyRepository.getInstance(AppDatabase.getInstance(context).cryptocurrencyDao()) } }

যে দেখতে পাচ্ছেন, একটি Dagger মডিউল তৈরি করতে এটিকে স্পেশাল @Module অ্যানোতেশন দিয়ে অ্যানোতেট করতে হয়। প্রজেক্টে সাধারণত একাধিক Dagger মডিউল থাকে। একটি সাধারণত অ্যাপ-ওয়াইড dependency প্রদান করে। এই AppModule অ্যাপ্লিকেশনের সারা জায়গায় ব্যবহৃত অবজেক্ট ইনিশিয়ালাইজ করতে ব্যবহৃত হবে, যেমন Room database, Retrofit, Shared Preference, ইত্যাদি।

উদাহরণস্বরূপ, একটি খুব সাধারণ পরিস্থিতি আলোচনা করি AppModule-এ Context অবজেক্ট প্রদান করার জন্য যেন অ্যাপের যেকোনো জায়গা থেকে এক্সেস করা যায়। কোডটি বিশ্লেষণ করে দেখি কীভাবে করা যায়।

আমাদের স্পেশাল Dagger অ্যানোতেশন @Provides ব্যবহার করতে হবে। এটি Dagger-কে বলে যে মেথডটি একটি নির্দিষ্ট টাইপের dependency প্রদান করে, আমাদের কেসে Context অবজেক্ট। তাই যখন অ্যাপের যেকোনো জায়গায় Context inject করার রিকোয়েস্ট করা হবে, AppModule হলো সেই জায়গা যেখানে Dagger এটা খুঁজে পাবে। মেথডের নাম কিছু মানে রাখে না, Dagger শুধু রিটার্ন টাইপের apie কেয়ার করে। provide প্রিফিক্স দিয়ে নাম রাখা সাধারণ অভ্যাস, কিন্তু এটা যেকোনো কিছুই হতে পারে।

একই মেথডে ব্যবহৃত @Singleton অ্যানোতেশন Dagger অ্যানোতেশন নয়। এটি javax প্যাকেজের ভিতরে থাকে। এই অ্যানোতেশন Dagger-কে বলে যে সেই dependency-এর শুধু একটি ইনস্ট্যান্স থাকবে।

আপনাকে বয়লারপ্লেট কোড লিখতে হবে না অন্য ইনস্ট্যান্স ইতিমধ্যে উপলব্ধ কিনা চেক করার জন্য। কোড জেনারেট করার সময় Dagger সেটা হ্যান্ডেল করবে এই অ্যানোতেশনের কারণে। লক্ষ্য করুন আমাদের AppModule অন্য একটি মডিউল ViewModelsModule ইনক্লুড করে। এখন সেটি তৈরি করি।

ViewModelsModule ক্লাস মডিউল তৈরি করুন। এই মডিউল পুরো অ্যাপ্লিকেশনের জন্য ViewModels প্রদান করার দায়বদ্ধ থাকবে।

/** * ViewModels প্রদান করার জন্য দায়বদ্ধ। */ @Module abstract class ViewModelsModule { // আমরা ViewModel ক্লাসের এই ইমপ্লিমেন্টেশনকে নিতে চাই এবং এটিকে একটি injectable map-এ উপলব্ধ করাতে চাই MainViewModel::class কি হিসেবে। @Binds @IntoMap @ViewModelKey(MainViewModel::class) // আমরা multibound map-এ রস্ট্রিকশন ব্যবহার করছি @ViewModelKey অ্যানোতেশন দিয়ে تعريف করা, এবং যদি কোনো রস্ট্রিকশন দরকার না হয়, তবে Dagger-র প্রদত্ত @ClassKey অ্যানোতেশন ব্যবহার করব। abstract fun bindMainViewModel(mainViewModel: MainViewModel): ViewModel @Binds @IntoMap @ViewModelKey(AddSearchViewModel::class) abstract fun bindAddSearchViewModel(addSearchViewModel: AddSearchViewModel): ViewModel @Binds abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory }

এই মডিউল Dagger 2-এর map multi bindings ফিচার ব্যবহার করে। এটি ব্যবহার করে, আমরা আমাদের পছন্দের অবজেক্টগুলো একটি ম‍্যাপে কন্ট্রিবিউট করি যা অ্যাপের যেকোনো জায়গায় injectable হয়ে যায়। Dagger অ্যানোতেশন @Binds, @IntoMap এবং আমাদের কাস্টম অ্যানোতেশন @ViewModelKey (এটি আমরা তৈরি করব) কম্বিনেশন ব্যবহার করে, আমরা আমাদের ম‍্যাপে একটি এন্ট্রি তৈরি করি কি MainViewModel::class এবং ভ্যালু MainViewModel ইনস্ট্যান্স। আমরা স্পেসিফিক factory বাইন্ড করি কিছু কমন ViewModelFactory ক্লাসের সাহায্যে। আমাদের এই ক্লাস তৈরি করতে হবে।

একটি কাস্টম অ্যানোতেশন ক্লাস ViewModelKey তৈরি করুন।

/** * একটি অ্যানোতেশন ক্লাস যা Dagger-কে বলে যে এটি multibound maps-এ কি নির্ধারণ করতে ব্যবহার করা যায়। */ @MustBeDocumented @Target( AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER ) @Retention(AnnotationRetention.RUNTIME) @MapKey annotation class ViewModelKey(val value: KClass<out ViewModel>) // আমরা শুধু সেই ক্লাসগুলো ব্যবহার করতে পারি যা ViewModel থেকে ইনহেরিট করে।

এই ক্লাস ViewModelsModule-এ ViewModels বাইন্ড করার জন্য ব্যবহৃত হয়। স্পেসিফিক অ্যানোতেশন @ViewModelKey আমাদের ম‍্যাপের কি রিপ্রেজেন্ট করে। আমাদের কি শুধু একটি ক্লাস হতে পারে যা ViewModel-এ서 ইনহেরিট করে।

ViewModelFactory ক্লাস তৈরি করুন।

/** * Class to Provider Map অটো-জেনারেট করার জন্য ফ্যাক্টরি। * আমরা Provider<T> ব্যবহার করি একটি injectable অবজেক্ট পরে তৈরি করার জন্য। */ @Suppress("UNCHECKED_CAST") @Singleton class ViewModelFactory @Inject constructor(private val viewModelsMap: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { var creator: Provider<out ViewModel>? = viewModelsMap[modelClass] if (creator == null) { for (entry in viewModelsMap.entries) { if (modelClass.isAssignableFrom(entry.key)) { creator = entry.value break } } } if (creator == null) { throw IllegalArgumentException("Unknown model class $modelClass") } try { return creator.get() as T } catch (e: Exception) { throw RuntimeException(e) } } }

এই ViewModelFactory একটি ইউটিলিটি ক্লাস যা আপনাকে ডাইনামিক্যালি ViewModels তৈরি করতে সাহায্য করে। এখানে আপনি জেনারেটেড ম‍্যাপ আর্গুমেন্ট হিসেবে প্রদান করেন। create() মেথডটি ম‍্যাপ থেকে সঠিক ইনস্ট্যান্স পিক করতে পারবে।

ActivityBuildersModule ক্লাস মডিউল তৈরি করুন।

/** * সকল অ্যাক্টিভিটি যেগুলো Dagger @Inject ব্যবহার করতে চায় এখানে লিস্ট করা থাকবে। */ @Module abstract class ActivityBuildersModule { @ContributesAndroidInjector(modules = [MainListFragmetBuildersModule::class]) // কোথায় injection অ্যাপ্লাই হবে। abstract fun contributeMainActivity(): MainActivity @ContributesAndroidInjector abstract fun contributeAddSearchActivity(): AddSearchActivity }

এই মডিউল আপনার সকল অ্যাক্টিভিটি কনস্ট্রাক্ট করার জন্য দায়বদ্ধ। এটি আপনার এই ক্লাসে ডিফাইন করা সকল Activity-র জন্য AndroidInjector জেনারেট করবে। তারপর অবজেক্টগুলো অ্যাক্টিভিটিতে inject করা যাবে AndroidInjection.inject(this) ব্যবহার করে অ্যাক্টিভিটি লাইফসাইকেলের onCreate ফাংশনে। লক্ষ্য করুন এই মডিউল আরো একটি আলাদা মডিউল ব্যবহার করে যা ফ্র্যাগমেন্টের জন্য দায়বদ্ধ। সেটি এখন তৈরি করব।

MainListFragmetBuildersModule ক্লাস মডিউল তৈরি করুন।

/** * MainActivity-র সাথে সম্পর্কিত সকল ফ্র্যাগমেন্ট যেগুলো Dagger @Inject ব্যবহার করতে চায় এখানে লিস্ট করা থাকবে। */ @Module abstract class MainListFragmetBuildersModule { @ContributesAndroidInjector() // ফ্র্যাগমেন্টকে Dagger গ্রাফের সাথে অ্যাচ করে। abstract fun contributeMainListFragment(): MainListFragment }

এই মডিউল আপনার MainActivity-এর সাথে সম্পর্কিত সকল ফ্র্যাগমেন্ট বিল্ড করবে। এটি এই ক্লাসে ডিফাইন করা সকল Fragment-র জন্য AndroidInjector জেনারেট করবে। অবজেক্টগুলো ফ্র্যাগমেন্টে inject করা যাবে AndroidSupportInjection.inject(this) ব্যবহার করে ফ্র্যাগমেন্ট লাইফসাইকেলের onAttach ফাংশনে।

AppComponent ক্লাস কম্পোনেন্ট তৈরি করুন।

/** * অ্যাপের জন্য সিঙ্গলটন কম্পোনেন্ট ইন্টারফেস। এটি সব মডিউলকে একসাথে বাঁধে। * কম্পোনেন্ট অবজেক্টকে তাদের dependency-র সাথে কানেক্ট করতে ব্যবহৃত হয়। * Dagger অটো-জেনারেট করবে DaggerAppComponent যা Application-এ ইনিশিয়ালাইজেশনের জন্য ব্যবহৃত হয়। */ @Singleton @Component( modules = [ // AndroidSupportInjectionModule Dagger-এর একটি ক্লাস এবং আমাদের তৈরি করার দরকার নেই। // যদি ফ্র্যাগমেন্টে injection ব্যবহার করতে চান তবে AndroidSupportInjectionModule.class ব্যবহার করবেন অন্যথায় AndroidInjectionModule। AndroidSupportInjectionModule::class, AppModule::class, ActivityBuildersModule::class ] ) interface AppComponent { @Component.Builder // কম্পোনেন্ট ইনস্ট্যানশিয়েশনের জন্য ব্যবহৃত। interface Builder { @BindsInstance // আমাদের অ্যাপ্লিকেশন ইনস্ট্যান্সকে আমাদের Dagger গ্রাফের সাথে বাঁধুন। fun application(application: App): Builder fun build(): AppComponent } // অ্যাপ্লিকেশন যেটি মডিউল দ্বারা ঘোষিত dependency রিকোয়েস্ট করতে পারবে (@Inject অ্যানোতেশন দিয়ে) সেটিকে এখানে ডিক্লেয়ার করতে হবে ইনডিভিজুয়াল inject() মেথডস দিয়ে। fun inject(app: App) }

Component খুব গুরুত্বপূর্ণ ক্লাস। এটি উপরের সবকিছু একসাথে কাজ করতে সক্ষম করে। এটি অবজেক্টকে তাদের dependency-র সাথে কানেক্ট করে এটি করে। Dagger এই ইন্টারফেস ব্যবহার করে dependency injection পরফর্ম করার জন্য প্রয়োজনীয় কোড জেনারেট করবে।

একটি কম্পোনেন্ট ক্লাস তৈরি করতে আপনাকে Dagger অ্যানোতেশন @Component ব্যবহার করতে হবে। এটি মডিউলের একটি লিস্ট ইনপুট হিসেবে নেয়। অন্য অ্যানোতেশন @Component.Builder আমাদের কিছু ইনস্ট্যান্স কম্পোনেন্টের সাথে বাঁধতে দেয়।

তারপর গ্রাফ অবজেক্ট জেনারেট করুন।

এই মুহূর্তে আপনার সব মডিউল এবং কম্পোনেন্ট সেটআপ হয়েছে। আপনি আপনার গ্রাফ অবজেক্ট জেনারেট করতে পারেন Build → Make Module সিলেক্ট করে আপনার Android Studio IDE-তে। ভবিষ্যতের ধাপের জন্য এই জেনারেশন প্রয়োজন হবে।

এখন একটি Injectable ইন্টারফেস তৈরি করুন।

/** * এটি শুধু একটি প্লেইন খালি مارকার ইন্টারফেস, যা বলে অটোমেটিক্যালি অ্যাক্টিভিটি বা ফ্র্যাগমেন্ট inject করবে যদি সেগুলো এটি ইমপ্লিমেন্ট করে। */ interface Injectable

এটিও ভবিষ্যতের ধাপের জন্য প্রয়োজন হবে। Injectable ইন্টারফেস অ্যাক্টিভিটি বা ফ্র্যাগমেন্ট দ্বারা ইমপ্লিমেন্ট করা উচিত যেগুলো আমরা অটোমেটিক্যালি injectable করতে চাই।

একটি নতুন হেল্পার ক্লাস তৈরি করুন নাম AppInjector।

/** * এটি একটি সিম্পল হেল্পার ক্লাস প্রতিটি অ্যাক্টিভিটি বা ফ্র্যাগমেন্টে inject মেথড কল এড়াতে। */ object AppInjector { fun init(app: App) { // এখানে Dagger ইনিশিয়ালাইজ করি। DaggerAppComponent AppComponent-র থেকে অটো-জেনারেটেড। DaggerAppComponent.builder().application(app).build().inject(app) app.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks { override fun onActivityPaused(activity: Activity) { } override fun onActivityResumed(activity: Activity) { } override fun onActivityStarted(activity: Activity) { } override fun onActivityDestroyed(activity: Activity) { } override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle?) { } override fun onActivityStopped(activity: Activity) { } override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { handleActivity(activity) } }) } private fun handleActivity(activity: Activity) { if (activity is HasSupportFragmentInjector || activity is Injectable) { // inject() মেথড কল করলে Dagger dependency graph-এ সিঙ্গলটনগুলো লোকেট করবে মিলে যাওয়া রিটার্ন টাইপ খুঁজতে। // যদি পায়, তবে রেফারেন্সগুলো 각각 ফিল্ডে অ্যাসাইন করবে। AndroidInjection.inject(activity) } if (activity is FragmentActivity) { activity.supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentCreated(fragmentManager: FragmentManager, fragment: Fragment, savedInstanceState: Bundle?) { if (fragment is Injectable) { AndroidSupportInjection.inject(fragment) } } }, true) } } }

এটি শুধু একটি সিম্পল হেল্পার ক্লাস প্রতিটি অ্যাক্টিভিটি বা ফ্র্যাগমেন্টে inject মেথড কল এড়াতে।

পরবর্তী, App ক্লাস সেটআপ করুন যা আমরা আগে তৈরি করেছি।

class App : Application(), HasActivityInjector { @Inject // এটি একটি টাইপের জন্য উপযুক্ত ইনজেক্টর ফ্যাক্টরি খুঁজে বের করার Dagger মেশিনারি ইমপ্লিমেন্ট করে। lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity> override fun onCreate() { super.onCreate() // ইনিশিয়ালাইজ করুন অটোমেটিক্যালি অ্যাক্টিভিটি ও ফ্র্যাগমেন্ট inject করার জন্য যদি সেগুলো Injectable ইন্টারফেস ইমপ্লিমেন্ট করে। AppInjector.init(this) ... } // HasActivityInjector ইন্টারফেস দ্বারা অ্যাক্টিভিটির জন্য Dagger সেটআপ করার জন্য প্রয়োজন। override fun activityInjector(): AndroidInjector<Activity> = dispatchingAndroidInjector }

কারণ অ্যাপ্লিকেশনে অ্যাক্টিভিটি আছে, আমাদের HasActivityInjector ইন্টারফেস ইমপ্লিমেন্ট করতে হবে। আপনি Android Studio-র तरफে DaggerAppComponent-এ এরর দেখলে, সেটি কারণ আপনি নতুন ফাইল জেনারেট করেননি, যেমন পূর্বের ধাপে নির্দেশ করা হয়েছিল।

তাহলে, MainActivity সেটআপ করুন মেইন ViewModel factory inject করার জন্য এবং ফ্র্যাগমেন্ট injection-এর সাপোর্ট যোগ করার জন্য।

// এই অ্যাক্টivitির অন্তর্গত ফ্র্যাগমেন্ট inject সাপোর্ট করার জন্য HasSupportFragmentInjector ইমপ্লিমেন্ট করতে হবে। // এটিকে ইমপ্লিমেন্ট করতে হতো না, যদি আমাদের অ্যাক্টিভিটিতে কোনো ফ্র্যাগমেন্ট না থাকত বা ফ্র্যাগমেন্টগুলোকে কিছু inject করার দরকার নাই। class MainActivity : AppCompatActivity(), HasSupportFragmentInjector { @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment> @Inject lateinit var viewModelFactory: ViewModelProvider.Factory private lateinit var mainViewModel: MainViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ViewModelProviders থেকে ViewModel পাওয়া, এই অ্যাক্টিভিটি LifecycleOwner হিসেবে ব্যবহার করে। mainViewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java) ... } ... override fun supportFragmentInjector(): AndroidInjector<Fragment> = dispatchingAndroidInjector ... }

কারণ আমাদের অ্যাক্টিভিটিতে চাইল্ড ফ্র্যাগমেন্ট আছে, আমাদের HasSupportFragmentInjector ইন্টারফেস ইমপ্লিমেন্ট করতে হবে। আমাদের এটিও দরকার কারণ আমরা আমাদের ফ্র্যাগমেন্টে injection করার প্ল্যান করছি। আমাদের অ্যাক্টিভিটিকে জানা উচিত নয় কীভাবে এটি inject হচ্ছে। আমরা AndroidInjection.inject(this) কোড লাইন ব্যবহার করি onCreate() মেথড ওভাররাইড করলে।

inject() মেথড কল করলে Dagger 2 dependency graph-এ সিঙ্গলটনগুলো লোকেট করবে মিলে যাওয়া রিটার্ন টাইপ খুঁজতে। তবে আমাদের এখানে কোনো কোড লিখতে হবে না কারণ এটি আগে তৈরি AppInjector হেল্পার ক্লাস করছে যা আমরা আমাদের অ্যাপ্লিকেশন ক্লাসে ইনিশিয়ালাইজ করেছি।

তারপর, MainListFragment সেটআপ করুন মেইন ViewModel factory inject করার জন্য।

/** * A placeholder fragment containing a simple view. */ class MainListFragment : Fragment(), Injectable { ... @Inject lateinit var viewModelFactory: ViewModelProvider.Factory private lateinit var viewModel: MainViewModel ... override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) ... subscribeUi(activity!!) } ... private fun subscribeUi(activity: FragmentActivity) { // ViewModelProviders থেকে ViewModel পাওয়া, প্যারেন্ট অ্যাক্টিভিটি LifecycleOwner হিসেবে ব্যবহার করে। viewModel = ViewModelProviders.of(activity, viewModelFactory).get(MainViewModel::class.java) ... } }

অ্যাক্টিভিটির মত, যদি আমরা আমাদের ফ্র্যাগমেন্টকে injectable করতে চাই, তবে এর onAttach মেথডে কোড AndroidSupportInjection.inject(this) লিখতে হবে। কিন্তু আবারও এটি AppInjector হেল্পারের কাজ, তাই আমরা এটিকে স্কিপ করতে পারি। শুধু লক্ষ্য করুন যে আমাদের আগে তৈরি Injectable ইন্টারফেস অ্যাড করতে হবে হেল্পার কাজ করার জন্য।

অভিনন্দন, আমরা My Crypto Coins অ্যাপ প্রজেক্টে Dagger 2 ইমপ্লিমেন্ট করেছি। অবশ্য এই আর্টিকেল আপনার অ্যাপে Dagger 2 দ্রুত ডিপ্লয় করার একটি কুইক গাইড, কিন্তু গভীর কভারেজ নয়। আমি রিকমেন্ড করি আপনি এই টপিকে আরও রিসার্চ করুন যদি বেসিকস-এ লост ফিল করেন।

Repository

আপডেটেড "Kriptofolio" (পূর্বে "My Crypto Coins") অ্যাপের সোর্স কোড GitHub-এ চেক আউট করুন।

View Source On GitHub

Ačiū! Thanks for reading! I originally published this post for my personal blog www.baruckis.com on October 7, 2018.


  1. আইফোন অ্যাপ দিয়ে ফ্লাইট স্ট্যাটাস চেক করার পদ্ধতি

  2. পিসিতে টেলিগ্রাম নিরাপদে অ্যাক্সেস করার ধাপে ধাপে গাইড

  3. আইফোনে WhatsApp সিকিউর করুন: Face ID বা Touch ID দিয়ে লক করুন

  4. অ্যান্ড্রয়েড থেকে আইফোন ট্র্যাক করুন: ধাপে ধাপে গাইড