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

در قسمت اول، پروژه رو ساختیم و فایل های مربوطه به لایه نتورک رو ایجاد کردیم:

https://vrgl.ir/ez4b2


https://vrgl.ir/bFnon



قبل از شروع کد نویسی، شما باید با یک سری از مفاهیم Dagger آشنایی داشته باشید مثل Component، Module و... . اگه با دگر کار نکردید و مستقیم میخواید که با Dagger hilt کار کنید، باید این مفاهیمو یاد بگیرید که در ادامه بهش می پردازیم:

به طور کلی ۴ انوتیشن (annotation) اصلی وجود دارد:

  • Module
  • Component
  • Provides
  • Inject

برای درک بهتر، ماژول را به عنوان تامین کننده وابستگی ها در نظر میگیریم و از طریق کمپوننت، وابستگی ها را به مصرف کننده، مانند اکتیویتی می دهد.

کلاس ماژول از انوتیشن 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



در قسمت سوم وابستگی ها را پیاده سازی می کنیم.