توسعه دهنده نِیتیوْ(Native) موبایل ( اندروید/آی او اس(کنارگذاشته شده)) Moeindeveloper.ir
استفاده از Dagger hilt به صورت پروژه محور (قسمت اول)
بعد از چند وقت تصمیم گرفتم که یک سری مقاله برای تکمیل کردن دوره توسعه اپلیکیشن های مدرن اندرویدی منتشر کنم. در دوره قبلی به علت کمبود وقت، Dagger 2 رو آموزش ندادم و به جای اون از KOIN استفاده کردم؛ اما چند وقت پیش با شنیدن ارائه Dagger Hilt این فکر به سرم زد که بیام و ی مقاله پروژه محور(سورس کد بعد از دوره در گیتهاب قرار خواهد گرفت) راجبش منتشر کنم. با ی سرچ ساده ی آموزش ساده از MindOrks پیدا کردم که خیلی ساده آموزشو پیش برده که برای این سری مقاله اکثر قسمتاشو مثل اون پیش میریم!
قسمت ها:
قدم اول: کار اپلیکیشن
برای اینکه اپلیکیشن تقریبا واقعی به نظر بیاد، به یک Web API نیاز داریم. با ی سرچ ساده، دو API برای یکی از سریال های محبوبم Game of thrones پیاده کردم?:
قدم دوم: ساخت پروژه
شروع به ساخت یک اپلیکیشن در اندروید استدیو کردم و اسم پروژه رو Ice and Fire گذاشتم.
ساختار پوشه بندی اصلی:
کتابخانه های اضافه شده:
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-rc1'
برای استفاده از Motion layout از ورژن ۲ کانسترینت لایوت استفاده می کنم.
کتاب خانه های جت پک
//android Arch components:
implementation "androidx.concurrent:concurrent-futures-ktx:1.1.0-rc01"
def lifecycle_version = "2.2.0"
def arch_version = "2.1.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Annotation processor
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
def nav_version = "2.3.0"
//navigation
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
رتروفیت:
//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
دگر هیلت و ماژول لایف سایکل ( برای ویومدل):
//dagger hilt
implementation 'com.google.dagger:hilt-android:2.28-alpha'
kapt 'com.google.dagger:hilt-android-compiler:2.28-alpha'
//android lifecycle
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
در فایل Build.gradle ماژول پروژه و در قسمت Dependencies:
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
و در قسمت repositories :
repositories {
google()
jcenter()
mavenCentral()
}
و در فایل Build.gradle ماژول اپ:
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
پس از انجام این مراحل، گریدل خود را Sync کنید!
قدم سوم: ساخت لایه نتورک
برای شروع،دو کلاس برای برای اعلام وضعیت بین لایه نتورک و یو ای میسازیم و اسم اونو Resource و RequestStatus میذاریم:
enum class RequestStatus {
SUCCESS,
ERROR,
LOADING
}
data class Resource<out T>(val status: RequestStatus, val data: T?, val Message: String?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(RequestStatus.SUCCESS,data,null)
}
fun <T> error(msg: String, data: T?): Resource<T> {
return Resource(RequestStatus.ERROR,data,msg)
}
fun <T> loading(data: T?): Resource<T> {
return Resource(RequestStatus.LOADING,data,null)
}
}
}
سپس طبق مستندات API بالا، دیتاکلاس های نتورک رو میسازیم:
data class Book(
@SerializedName("authors")
val authors: List<String>,
@SerializedName("characters")
val characters: List<String>,
@SerializedName("country")
val country: String,
@SerializedName("isbn")
val isbn: String,
@SerializedName("mediaType")
val mediaType: String,
@SerializedName("name")
val name: String,
@SerializedName("numberOfPages")
val numberOfPages: Int,
@SerializedName("povCharacters")
val povCharacters: List<String>,
@SerializedName("publisher")
val publisher: String,
@SerializedName("released")
val released: String,
@SerializedName("url")
val url: String
)
data class Character(
@SerializedName("aliases")
val aliases: List<String>,
@SerializedName("allegiances")
val allegiances: List<Any>,
@SerializedName("books")
val books: List<String>,
@SerializedName("born")
val born: String,
@SerializedName("culture")
val culture: String,
@SerializedName("died")
val died: String,
@SerializedName("father")
val father: String,
@SerializedName("mother")
val mother: String,
@SerializedName("name")
val name: String,
@SerializedName("playedBy")
val playedBy: List<String>,
@SerializedName("povBooks")
val povBooks: List<String>,
@SerializedName("spouse")
val spouse: String,
@SerializedName("titles")
val titles: List<String>,
@SerializedName("tvSeries")
val tvSeries: List<String>,
@SerializedName("url")
val url: String
)
data class House(
@SerializedName("ancestralWeapons")
val ancestralWeapons: List<String>,
@SerializedName("cadetBranches")
val cadetBranches: List<String>,
@SerializedName("coatOfArms")
val coatOfArms: String,
@SerializedName("currentLord")
val currentLord: String,
@SerializedName("diedOut")
val diedOut: String,
@SerializedName("founded")
val founded: String,
@SerializedName("founder")
val founder: String,
@SerializedName("heir")
val heir: String,
@SerializedName("name")
val name: String,
@SerializedName("overlord")
val overlord: String,
@SerializedName("region")
val region: String,
@SerializedName("seats")
val seats: List<String>,
@SerializedName("swornMembers")
val swornMembers: List<String>,
@SerializedName("titles")
val titles: List<String>,
@SerializedName("url")
val url: String,
@SerializedName("words")
val words: String
)
data class Quote(
@SerializedName("character")
val character: String,
@SerializedName("quote")
val quote: String
)
حالا نوبت میرسه به ساخت interface ها:
interface IceAndFireApiHelper {
suspend fun getBooks(): Response<List<Book>>
suspend fun getBook(
bookID: Int
): Response<Book>
suspend fun getCharacters(): Response<List<Character>>
suspend fun filterCharacters(
name: String?,
gender: String?,
culture: String?,
isAlive: Boolean?
): Response<List<Character>>
suspend fun getCharacter(characterID: Int): Response<Character>
suspend fun getHouses(): Response<List<House>>
suspend fun getHouse(houseID: Int): Response<House>
}
class IceAndFireApiImpl(private val service: IceAndFireApiService) : IceAndFireApiHelper {
override suspend fun getBooks(): Response<List<Book>> = service.getBooks()
override suspend fun getBook(bookID: Int): Response<Book> = service.getBook(bookID)
override suspend fun getCharacters(): Response<List<Character>> = service.getCharacters()
override suspend fun filterCharacters(
name: String?,
gender: String?,
culture: String?,
isAlive: Boolean?
): Response<List<Character>> = service.filterCharacters(name,gender,culture,isAlive)
override suspend fun getCharacter(characterID: Int): Response<Character> = service.getCharacter(characterID)
override suspend fun getHouses(): Response<List<House>> = service.getHouses()
override suspend fun getHouse(houseID: Int): Response<House> = service.getHouse(houseID)
}
interface IceAndFireApiService {
@GET("api/books")
suspend fun getBooks(): Response<List<Book>>
@GET("api/books/{id}")
suspend fun getBook(
@Path("id") bookID: Int
): Response<Book>
@GET("api/characters")
suspend fun getCharacters(): Response<List<Character>>
@GET("api/characters")
suspend fun filterCharacters(
@Query("name") name: String?,
@Query("gender") gender: String?,
@Query("culture") culture: String?,
@Query("isAlive") isAlive: Boolean?
): Response<List<Character>>
@GET("api/characters/{id}")
suspend fun getCharacter(@Path("id") characterID: Int): Response<Character>
@GET("api/houses")
suspend fun getHouses(): Response<List<House>>
@GET("api/houses/{id}")
suspend fun getHouse(@Path("id") houseID: Int): Response<House>
}
interface QuotesApiHelper {
suspend fun getQuotes(): Response<Quote>
suspend fun filterQuotes(
character: String
): Response<Quote>
}
class QuotesApiImpl(private val service: QuotesApiService): QuotesApiHelper {
override suspend fun getQuotes(): Response<Quote> = service.getQuotes()
override suspend fun filterQuotes(character: String): Response<Quote> = service.filterQuotes(character)
}
interface QuotesApiService {
@GET("quotes")
suspend fun getQuotes(): Response<Quote>
@GET("quotes")
suspend fun filterQuotes(
@Query("char") character: String
): Response<Quote>
}
برای استفاده از coroutines از suspend fun استفاده میکنیم، و حتما در نظر داشته باشید که از ورژن 2.6+ رتروفیت استفاده کنید که کروتینز رو پشتیبانی کنه!
خب برای این قسمت فک کنم تا اینجا کافی باشه! در قسمت بعدی مفاهیم دگر رو به صورت کلی بررسی خواهیم کرد و پروژه رو با دگر کانفیگ میکنیم!
مطلبی دیگر از این انتشارات
ذخیره سازی اطلاعات در آندروید — SharedPreferences
مطلبی دیگر از این انتشارات
پنج مهارت برتر برای یک برنامهنویس موفق
مطلبی دیگر از این انتشارات
مجموعه تقلب مصاحبه شغلی برنامه نویس اندروید - قسمت دوم