توسعه دهنده نِیتیوْ(Native) موبایل ( اندروید/آی او اس(کنارگذاشته شده)) Moeindeveloper.ir
استفاده از Dagger Hilt به صورت پروژه محور (قسمت دوم)
در قسمت اول، پروژه رو ساختیم و فایل های مربوطه به لایه نتورک رو ایجاد کردیم:
قبل از شروع کد نویسی، شما باید با یک سری از مفاهیم 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=".IceAndFireApplication"
...
</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="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
کانفیگ اولیه ماژول:
class IceAndFireModule {
}
همانطور که گفتیم، باید برای دگر ماژول را مشخص کنیم، پس از انوتیشن Dagger استفاده می کنیم:
@Module
class IceAndFireModule
حالا باید پل ارتباطی یا همان Component را به ماژول معرفی کنیم، در دگر ۲ باید ماژول را به کمپوننت معرقی میکردیم اما حالا در دگر هیلت، با توجه به اینکه خود هیلت کامپوننت را میسازد، باید کامپوننت را با استفاده از انوتیشن InstallIn مشخص کنیم:
@Module
@InstallIn(ApplicationComponent::class)
class IceAndFireModule
در اینجا ما میخوایم که وابستگی های ماژول من در سطح Appplication باشه پس از ApplicationComponent استفاده میکنم.
برای اطلاعات بیشتر در مورد ترتیب بندی کمپوننت ها، به عکس زیر دقت کنید:
برای اطلاعات بیشتر:
در قسمت سوم وابستگی ها را پیاده سازی می کنیم.
مطلبی دیگر از این انتشارات
تست و CI در برنامه نویسی اندروید
مطلبی دیگر از این انتشارات
پروژه های خود را با خرد کردن به انجام برسونید !
مطلبی دیگر از این انتشارات
ویجت 18 ( Row/Column در فلاتر)