استفاده از Dagger Hilt به صورت پروژه محور (قسمت دوم)

در قسمت اول، پروژه رو ساختیم و فایل های مربوطه به لایه نتورک رو ایجاد کردیم: https://vrgl.ir/ez4b2 https://vrgl.ir/bFnon قبل از شروع کد نویسی، شما باید با یک سری از مفاهیم Dagger آشنایی داشته باشید مثل Component، Module و... . اگه با دگر کار نکردید و مستقیم میخواید که با Dagger hilt کار کنید، باید این مفاهیمو یاد بگیرید که در ادامه بهش می پردازیم:به طور کلی ۴ انوتیشن (annotation) اصلی وجود دارد:ModuleComponentProvidesInjectبرای درک بهتر، ماژول را به عنوان تامین کننده وابستگی ها در نظر میگیریم و از طریق کمپوننت، وابستگی ها را به مصرف کننده، مانند اکتیویتی می دهد.کلاس ماژول از انوتیشن Module استفاده می کند که به دگر بفهماند این یک کلاس ماژول است و عملیات های مربوطه را انجام دهد! انوتیشن Provides برای تعریف و تامین یک وابستگی داخل کلاس ماژول به کار میره!کمپوننت یک اینترفیسه که با انوتیشن Component مشخص میشه ولی دیگه در Dagger hilt لازم نیست که اونو بسازیم و دگر هلیت اونو به صورت اتوماتیک generate می کنه!و در آخر از انوتیشن Inject برای تعریف یک وابستگی داخل مصرف کننده به کار میره!پیاده سازی Dagger Hilt:قبل از شروع هر کاری، یک کلاس از نوع Application بسازید و در اندروید منیفست قرار بدید:class IceAndFireApplication: Application()<application ... android:name=&quot.IceAndFireApplication&quot ... </application>و سپس انوتیشن @HiltAndroidAppرا در بالای کلاس اپلیکیشن خود قرار دهید:@HiltAndroidApp class IceAndFireApplication: Application()در بخش نتورک، دو کلاس IceAndFireApiImpl و QuotesApiImpl که نیاز به تزریق دو اینترفیس QuotesApiService و IceAndFireApiService دارند؛ بنابراین، انوتیشین Inject را در Constructor قرار می دهیم:class IceAndFireApiImpl @Inject constructor(private val service: IceAndFireApiService) : IceAndFireApiHelper { ... }class QuotesApiImpl @Inject constructor(private val service: QuotesApiService): QuotesApiHelper { ... }برای ویومدل خود یک ریپازیتوری میسازیم: class MainRepository @Inject constructor(private val iceAndFireApiHelper: IceAndFireApiHelper, private val quotesApiHelper: QuotesApiHelper) { suspend fun getBooks(): Response<List<Book>> = iceAndFireApiHelper.getBooks() suspend fun getBook(bookID: Int): Response<Book> = iceAndFireApiHelper.getBook(bookID) suspend fun getCharacters(): Response<List<Character>> = iceAndFireApiHelper.getCharacters() suspend fun filterCharacters( name: String?, gender: String?, culture: String?, isAlive: Boolean? ): Response<List<Character>> = iceAndFireApiHelper.filterCharacters(name,gender,culture,isAlive) suspend fun getCharacter(characterID: Int): Response<Character> = iceAndFireApiHelper.getCharacter(characterID) suspend fun getHouses(): Response<List<House>> = iceAndFireApiHelper.getHouses() suspend fun getHouse(houseID: Int): Response<House> = iceAndFireApiHelper.getHouse(houseID) suspend fun getQuotes(): Response<Quote> = quotesApiHelper.getQuotes() suspend fun filterQuotes(character: String): Response<Quote> = quotesApiHelper.filterQuotes(character) }ریپازیتوری اصلی ما، IceAndFireApiHelper و QuotesApiHelper را نیاز دارد که باید به آن تزریق کنیم!کلاس ویومدل ما بدین شکل است: class MainViewModel @ViewModelInject constructor(private val mainRepository: MainRepository): ViewModel() { private val _quote = MutableLiveData<Resource<Quote>>() val quote : LiveData<Resource<Quote>> get() = _quote private val _books = MutableLiveData<Resource<List<Book>>>() val books : LiveData<Resource<List<Book>>> get() = _books private val _characters = MutableLiveData<Resource<List<Character>>>() val characters : LiveData<Resource<List<Character>>> get() = _characters private val _houses = MutableLiveData<Resource<List<House>>>() val houses : LiveData<Resource<List<House>>> get() = _houses init { getQuote() getBooks() getCharacters() getHouses() } fun getQuote(){ viewModelScope.launch { _quote.postValue(Resource.loading(null)) mainRepository.getQuotes().let {response -> if (response.isSuccessful) { _quote.postValue(Resource.success(response.body())) } else _quote.postValue(Resource.error(response.errorBody().toString(),null)) } } } fun getBooks(){ viewModelScope.launch { _books.postValue(Resource.loading(null)) mainRepository.getBooks().let {response -> if (response.isSuccessful) { _books.postValue(Resource.success(response.body())) } else _books.postValue(Resource.error(response.errorBody().toString(),null)) } } } fun getCharacters(){ viewModelScope.launch { _characters.postValue(Resource.loading(null)) mainRepository.getCharacters().let {response -> if (response.isSuccessful) { _characters.postValue(Resource.success(response.body())) } else _characters.postValue(Resource.error(response.errorBody().toString(),null)) } } } fun getHouses(){ viewModelScope.launch { _houses.postValue(Resource.loading(null)) mainRepository.getHouses().let {response -> if (response.isSuccessful) { _houses.postValue(Resource.success(response.body())) } else _houses.postValue(Resource.error(response.errorBody().toString(),null)) } } } }برای تزریق وابستگی از ViewModelInject استفاده می کنیم که با استفاده از کتابخانه های زیر که به گریدل اضافه کردیم امکان پذیره:implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02' kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'و در آخر برای چک کردن وضعیت اتصال، یک کلاس به نام NetworkHelper ایجاد می کنیم که به این صورته:class NetworkHelper(private val context: Context) { fun isNetworkConnected(): Boolean { var result = false val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val networkCapabilities = connectivityManager.activeNetwork ?: return false val activeNetwork = connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false result = when { activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true else -> false } } else { connectivityManager.run { connectivityManager.activeNetworkInfo?.run { result = when (type) { ConnectivityManager.TYPE_WIFI -> true ConnectivityManager.TYPE_MOBILE -> true ConnectivityManager.TYPE_ETHERNET -> true else -> false } } } } return result } }و در آخر فراموش نکنید که این دوتا دسترسی رو به اندروید منیفست اضافه کنید:<uses-permission android:name=&quotandroid.permission.ACCESS_NETWORK_STATE&quot /> <uses-permission android:name=&quotandroid.permission.INTERNET&quot/>کانفیگ اولیه ماژول:class IceAndFireModule { }همانطور که گفتیم، باید برای دگر ماژول را مشخص کنیم، پس از انوتیشن Dagger استفاده می کنیم:@Module class IceAndFireModuleحالا باید پل ارتباطی یا همان Component را به ماژول معرفی کنیم، در دگر ۲ باید ماژول را به کمپوننت معرقی میکردیم اما حالا در دگر هیلت، با توجه به اینکه خود هیلت کامپوننت را میسازد، باید کامپوننت را با استفاده از انوتیشن InstallIn مشخص کنیم:@Module @InstallIn(ApplicationComponent::class) class IceAndFireModuleدر اینجا ما میخوایم که وابستگی های ماژول من در سطح Appplication باشه پس از ApplicationComponent استفاده میکنم.برای اطلاعات بیشتر در مورد ترتیب بندی کمپوننت ها، به عکس زیر دقت کنید:برای اطلاعات بیشتر: https://dagger.dev/hilt/components.html در قسمت سوم وابستگی ها را پیاده سازی می کنیم.