گرفتن داده از Restful API دارای چندین شی JSON – اندروید کاتلین(Kotlin)

گرفتن داده از Restful API دارای چندین شی JSON – اندروید کاتلین(Kotlin)

توی این مقاله با هم یک اپ اندرویدی درست میکنیم که با استفاده از Restful API چندین Object از جنس JSON میگیره. درواقع یک برنامه واقعه نگاری برای اپیدمی کورونا ویروس (Covid-19) درست میکنیم که لیستی از کشور هایی رو به ما میده که دچار این اپیدمی شده اند.عکس برنامه ای که تا اخر این مقاله مینویسیم (پایین تر لینک سورس رو گذاشتم)پیش نیاز های این اموزش:اندروید استودیواشنایی با اندرویدمفاهیم اولیه کتابخوانه Retrofitاشنایی اولیه با JSON(اگر این پیش نیاز هارو داشته باشید فهمیدن مفاهیم براتون اسون تر میشه، ولی اگر هم ندارید اشکالی نداره)برای شروع یک پروژه اندروید بسازید، مراحل ساخت:اندروید استودیو رو باز کنید.روی دکمه Start a new Android Studio Project کلیک کنید.در بخش Select a Project Template گزینه Empty Activity رو اتخواب کنید.در بخش Configure Your Project یک نام برای پروژه خود انتخواب کنید و زبان پروژه خود را روی Kotlin قرار بدید و روی دکمه FINISH کلیک کنید تا پروژه ساخته بشه.بعد از اینکه پروژتون ساخته شد، نیاز هستش که چند وابستگی که جلو تر ازشون استفاده میکنیم رو به فایل build.gradle اضافه کنید.وابستگی زیر برای تم (Theme) و نیازمندی های کتابخوانه Retrofit هست.implementation ‘com.google.android.material:material:1.1.0’
implementation ‘com.squareup.picasso:picasso:2.71828’
implementation ‘com.squareup.retrofit2:retrofit:2.5.0’
implementation ‘com.squareup.retrofit2:converter-gson:2.5.0’حالا میریم سراغ فایل activity_main.xml و یک recycler view به اون اضافه میکنیم. این view قرار یک لیستی از کشور هارو برای ما نمایش بده.کد داخل اون به این شکل در میاد<?xml version=&quot1.0&quot encoding=&quotutf-8&quot?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quothttp://schemas.android.com/apk/res/android&quot
xmlns:app=&quothttp://schemas.android.com/apk/res-auto&quot
xmlns:tools=&quothttp://schemas.android.com/tools&quot
android:layout_width=&quotmatch_parent&quot
android:layout_height=&quotmatch_parent&quot
tools:context=&quot.MainActivity&quot>

<androidx.recyclerview.widget.RecyclerView
android:layout_width=&quotmatch_parent&quot
android:[email protected]+id/country_recycler&quot
android:layout_height=&quotmatch_parent&quot
app:layout_constraintBottom_toBottomOf=&quotparent&quot
app:layout_constraintEnd_toEndOf=&quotparent&quot
app:layout_constraintStart_toStartOf=&quotparent&quot
app:layout_constraintTop_toTopOf=&quotparent&quot />
</androidx.constraintlayout.widget.ConstraintLayout>چیزی که توی محیط دیزاین میبینید احتمالا شبیه به اینه.با استفاده از متریال کامپوننت ها که قبل تر اضافه کردیم، استایل داخل فایل style.xml رو به استایل زیر تغییر بدید:<resources>
<!– Base application theme. –>
<style name=&quotAppTheme&quot parent=&quotTheme.MaterialComponents.Light.DarkActionBar.Bridge&quot>
<!– Customize your theme here. –>
<item name=&quotcolorPrimary&quot>@color/colorPrimary</item>
<item name=&quotcolorPrimaryDark&quot>@color/colorPrimaryDark</item>
<item name=&quotcolorAccent&quot>@color/colorAccent</item>
</style>
</resources>به عنوان قدم بعدی، از اونجایی که پروژه ما نیاز به دسترسی به منابعی در اینترنت داره ما باید داخل فایل manifest.xml اجازه دسترسی به اینترنت رو بگیریم. برای انجام اینکار کد های زیر رو اون اضافه میکنیم:<uses-permission android:name=&quotandroid.permission.ACCESS_NETWORK_STATE&quot/>
<uses-permission android:name=&quotandroid.permission.INTERNET&quot/>همون طور که قبل تر گفتم هدف ما توی این اموزش اینه که از یک API اطلاعاتی که دارای چندین شی هست رو واکشی کنیم. این داده ها به صورت json هستن:{&quotupdated&quot:1600770942831,
&quotcountry&quot:&quotAfghanistan&quot,
&quotcountryInfo&quot:
{&quot_id&quot:4,
&quotiso2&quot:&quotAF&quot,
&quotiso3&quot:&quotAFG&quot,
&quotlat&quot:33,
&quotlong&quot:65,
&quotflag&quot:&quothttps://disease.sh/assets/img/flags/af.png&quot},
&quotcases&quot:39096,
&quottodayCases&quot:22,
&quotdeaths&quot:1445,
&quottodayDeaths&quot:1,
&quotrecovered&quot:32576,
&quottodayRecovered&quot:0,
&quotactive&quot:5075,&quotcritical&quot:93,
&quotcasesPerOneMillion&quot:999,
&quotdeathsPerOneMillion&quot:37,
&quottests&quot:109068,
&quottestsPerOneMillion&quot:2788,
&quotpopulation&quot:39119903,
&quotcontinent&quot:&quotAsia&quot,
&quotoneCasePerPeople&quot:1001,
&quotoneDeathPerPeople&quot:27073,
&quotoneTestPerPeople&quot:359,
&quotactivePerOneMillion&quot:129.73,
&quotrecoveredPerOneMillion&quot:832.72,
&quotcriticalPerOneMillion&quot:2.38}نمونه واکشی شده بالا دارای 2 شی json هست که به بخش های زیر تقسیم میشوند:اطلاعت کشوراطلاعات جزئی تر کشورقبل از شروع این بخش بیاید فلدر java رو Package بندی کنیم.ممکنه این سوال براتون پیش بیاد که “چرا باید Package بندی کنم” در پاسخ میتونم بگم برای بهینه سازی معماری پروژه که کمک میکنه كد پروژه بيشتر قابل درك بشه هم قابل استفاده مجدد بشه. پس بیاید شروع کنیم…توی Package خودتون که شامل فایل MainActivity.kt میشه کلیک راست کنید، در منوی New گزینه Package رو اتخواب کنید. و پکیج هایی با نام های زیر بسازید.activitiesmodelsserviceshelpersفایل MainActivity.kt رو به پکیج activities منتقل کنید.نمونه نهایی پکیج بندیاگر عکس ایکن پکیج هاتون فرق داره مشکلی نیست، برای من به خاطر اینکه تم برای اندروید استودیو نصب کردم بر اساس اسم فولدر تغییر میکنه.برای اینکه داده رو بگیریم و وارد برنامه کنیمش نیازه که مشخص کنیم که چه نوع داده ای رو قراره بگیریم. این کار معمولا سخت ترین بخش هست، چون وقتی نمونه کلاس ها دچار پیچیدگی میشه کار رو براتون سخت میکنه.من ساده ترین راه رو با ستفاده از داده واکشی شده از API برای طراحی ساختار کلاستون بهتون نشون میدم.توی Android Studio در تب File روی گزینه Setting کلیک کنید. (ميتونيد از Ctrl+Alt+S استفاده کنید)وارد بخش Plugins بشید و عبارت (Json To Kotlin -Seal) رو سرچ و نصب کنید.این پلاگین کمکمون میکنه که بتونیم به سادگی از هر نمونه واکشی شده کلاس کاتلین بسازیم.درصورت نیاز اندروید استودیو رو Restart کنید.در پکیج models کلیک راست کنید و در منوی new گزینه ی Kotlin data class from JSON رو انتخواب کنید.یک نام برای کلاس انتخواب کنید (مثلا من “Country” نوشتم) و روی دکمه Advanced کلیک کنید. توی پنجره باز شده 4 تب وجود داره، تب Other رو باز کنید و گزینه Enable Inner Class Model رو فعال کنید.با این کار شما دارید به پلاگین میگید که کلاس های کاتلینی که از روی شی JSON ساخته شده رو داخل یک کلاس وارد کنه.کلاس ساخته شده به شکل زیر خواهد بود:package com.musicoverflow.restapitest.models

data class Country(
val active: Int,
val activePerOneMillion: Double,
val cases: Int,
val casesPerOneMillion: Int,
val continent: String,
val country: String,
val countryInfo: CountryInfo,
val critical: Int,
val criticalPerOneMillion: Double,
val deaths: Int,
val deathsPerOneMillion: Int,
val oneCasePerPeople: Int,
val oneDeathPerPeople: Int,
val oneTestPerPeople: Int,
val population: Int,
val recovered: Int,
val recoveredPerOneMillion: Double,
val tests: Int,
val testsPerOneMillion: Int,
val todayCases: Int,
val todayDeaths: Int,
val todayRecovered: Int,
val updated: Long
) {
data class CountryInfo(
val _id: Int,
val flag: String,
val iso2: String,
val iso3: String,
val lat: Int,
val long: Int
)
}این مدل رو موقع کار با Retrofit استفاده میکنیم.در پکیج services یک interface میسازیم و نام اون رو CountryService.kt میذاریم. این اینترفیس شامل فانکشنی میشه که با استفاده از retrofit لیستی از تمامی کشور ها میگیره.داخل اون این کد رو قرار میدیم: @GET(&quotcountries&quot)
fun getAffectedCountryList () : Call<List<Country>>حالا با استفاده از retrofit و اینترفیسی که ساختیم، یک ابجکت کلاس میسازیم که کمکمون میکنه سرویس هامون رو بسازیم. کد داخل ابجکت: private const val URL =&quothttps://disease.sh/v2/&quot
private val okHttp = OkHttpClient.Builder()

private val builder = Retrofit.Builder().baseUrl(URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttp.build())

private val retrofit = builder.build()

fun <T> buildService (serviceType :Class<T>):T{
return retrofit.create(serviceType)
}کاری که درواقع کد بالا انجام میده اینه که با استفاده از “OkHttp client” میاد retrofit رو نمونه سازی میکنه. که باعث میشه اینترفیس ما کار کنه.در قدم بعدی، داخل پکیج helper یک Adapter میسازیم که مسئول اضافه کردن ایتم هامون بهRecycler view هستش. کد داخل آن به شکل زیر است:class CountriesAdapter(private val countriesList: List<Country>) :RecyclerView.Adapter<CountriesAdapter.ViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.country_item,parent,false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return countriesList.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Log.d(&quotResponse&quot, &quotList Count :${countriesList.size} &quot)

return holder.bind(countriesList[position])
}
class ViewHolder(itemView : View) :RecyclerView.ViewHolder(itemView) {

var imageView = itemView.findViewById<ImageView>(R.id.ivFlag)
var tvTitle = itemView.findViewById<TextView>(R.id.tvTitle)
var tvCases = itemView.findViewById<TextView>(R.id.tvCases)
fun bind(country: Country) {

val name =&quotCases :${country.cases.toString()}&quot
tvTitle.text = country.country
tvCases.text = name
Picasso.get().load(country.countryInfo.flag).into(imageView)
}

}
}به فانکشن bind که داخل کلاس ViewHolder هست توجه کنید. این تابع اطلاعات رو به عناصر مربوطشون متصل میکنه و همچنین عکس رو با استفاده از picasso (که قبلا به پروژه اضافه کردیم) لود میکنه.همونطور که احتمالا قبلا دیدید، برای اینکه پرچم کشور رو بگیریم باید Country info رو بگیریم، ما از این روش بهش دسترسی داریم:country.countryInfo.flagو برای گرفتن عکس:Picasso.get().load(country.countryInfo.flag).into(imageView)برای نمایش دادن داده هامون در recylcer view باید سرویسی که درست کردیم رو فراخوانی کنیم. داخل MainActivity تابعی به نام loadCountries بسازید. به شکل زیر:private fun loadCountries() {
val destinationService = ServiceBuilder.buildService(CountryService::class.java)
val requestCall =destinationService.getAffectedCountryList()
requestCall.enqueue(object : Callback<List<MyCountry>>{
override fun onResponse(call: Call<List<MyCountry>>, response: Response<List<MyCountry>>) {
Log.d(&quotResponse&quot, &quotonResponse: ${response.body()}&quot)
if (response.isSuccessful){
val countryList = response.body()!!
Log.d(&quotResponse&quot, &quotcountrylist size : ${countryList.size}&quot)
country_recycler.apply {
setHasFixedSize(true)
layoutManager = GridLayoutManager([email protected],2)
adapter = CountriesAdapter(response.body()!!)
}
}else{
Toast.makeText([email protected], &quotSomething went wrong ${response.message()}&quot, Toast.LENGTH_SHORT).show()
}
}
override fun onFailure(call: Call<List<MyCountry>>, t: Throwable) {
Toast.makeText([email protected], &quotSomething went wrong $t&quot, Toast.LENGTH_SHORT).show()
}
})
}این تابع مسئول اتصال داده های ما به صورت async با استفاده از enqueue در retrofit و کمک service builder هست.بعد برسی میکنیم که درخواست ارسالی با موفقیت پاسخ داده شده باشه، بعد بدنه پاسخ رو به عنوان لیست به اداپتر وارد میکنیم. و حالا میتونید این فانکشن رو هر جایی که میخواید صدا بزنید. من خودم توی oncreate صدا میزنمش. کدش به صورت زیر هست:override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loadCountries()
}نمونه همین پروژه (با کمی تغییر)منبع امیدوارم این مقاله به کارتون امده باشه. نظرتون رو توی کامنت بهم بگید.موفق باشد.

Author: admin

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *