Android Kotlin Fundamentals 08.1: دریافت داده از اینترنت

این کد لبه بخشی از دوره آموزشی Android Kotlin Fundamentals است. اگر به ترتیب روی کدها کار کنید، بیشترین ارزش را از این دوره خواهید گرفت. همه کد لبه های دوره در صفحه فرود کد لبه های Android Kotlin Fundamentals فهرست شده اند.

مقدمه

تقریباً هر برنامه اندرویدی که می‌سازید نیاز به اتصال به اینترنت دارد. در این لبه کد و موارد بعدی، شما یک برنامه می سازید که به یک سرویس وب متصل می شود تا داده ها را بازیابی و نمایش دهد. شما همچنین بر اساس آنچه در کدهای گذشته در مورد ViewModel ، LiveData و RecyclerView آموخته اید، استفاده می کنید.

در این کد لبه، شما از کتابخانه های توسعه یافته جامعه برای ساخت لایه شبکه استفاده می کنید. این کار واکشی داده‌ها و تصاویر را بسیار ساده می‌کند و همچنین به برنامه کمک می‌کند تا با برخی از بهترین شیوه‌های اندروید، مانند بارگذاری تصاویر در یک رشته پس‌زمینه و ذخیره تصاویر بارگذاری‌شده، مطابقت داشته باشد. برای بخش‌های ناهمزمان یا غیرمسدود در کد، مانند مکالمه با لایه خدمات وب، برنامه را تغییر می‌دهید تا از برنامه‌های کاتلین استفاده کند. همچنین اگر اینترنت کند یا در دسترس نباشد، رابط کاربری برنامه را به روز می کنید تا به کاربر اطلاع دهید که چه اتفاقی می افتد.

آنچه از قبل باید بدانید

  • نحوه ایجاد و استفاده از قطعات
  • نحوه پیمایش بین قطعات و استفاده از safeArgs برای انتقال داده ها بین قطعات.
  • نحوه استفاده از اجزای معماری از جمله تبدیل‌های ViewModel ، ViewModelProvider.Factory ، LiveData و LiveData .
  • نحوه استفاده از کوروتین ها برای کارهای طولانی مدت

چیزی که یاد خواهید گرفت

  • وب سرویس REST چیست.
  • استفاده از کتابخانه Retrofit برای اتصال به وب سرویس REST در اینترنت و دریافت پاسخ.
  • استفاده از کتابخانه Moshi برای تجزیه پاسخ JSON به یک شی داده.

کاری که خواهی کرد

  • یک برنامه شروع را تغییر دهید تا یک درخواست API سرویس وب ایجاد کند و پاسخ را مدیریت کند.
  • با استفاده از کتابخانه Retrofit یک لایه شبکه برای برنامه خود پیاده کنید.
  • پاسخ JSON را از وب سرویس به داده های زنده برنامه خود با کتابخانه Moshi تجزیه کنید.
  • از پشتیبانی Retrofit برای کوروتین ها برای ساده کردن کد استفاده کنید.

در این کد لبه (و کدهای زیر)، شما با یک برنامه شروع به نام MarsRealEstate کار می کنید که املاکی را برای فروش در مریخ نشان می دهد. این برنامه برای بازیابی و نمایش داده های ملک، از جمله جزئیاتی مانند قیمت و اینکه آیا ملک برای فروش یا اجاره در دسترس است، به یک وب سرویس متصل می شود. تصاویری که هر ملک را نشان می دهد، عکس های واقعی از مریخ است که از مریخ نوردهای ناسا گرفته شده است.

نسخه برنامه‌ای که در این کد لبه می‌سازید فلاش بصری زیادی نخواهد داشت: بر روی بخش لایه شبکه برنامه تمرکز می‌کند تا به اینترنت متصل شود و داده‌های دارایی خام را با استفاده از یک سرویس وب دانلود کند. برای اطمینان از اینکه داده ها به درستی بازیابی و تجزیه می شوند، فقط تعداد ویژگی های مریخ را در یک نمای متنی چاپ می کنید:

.

معماری برنامه MarsRealEstate دارای دو ماژول اصلی است:

  • یک قطعه نمای کلی، که شامل شبکه‌ای از تصاویر ویژگی کوچک است که با RecyclerView ساخته شده است.
  • یک قطعه نمای جزئیات، حاوی اطلاعات مربوط به هر ویژگی.

این برنامه برای هر قطعه یک ViewModel دارد. برای این کد لبه، شما یک لایه برای سرویس شبکه ایجاد می کنید و ViewModel مستقیماً با آن لایه شبکه ارتباط برقرار می کند. این شبیه کاری است که در کدهای قبلی هنگام ارتباط ViewModel با پایگاه داده Room انجام دادید.

نمای کلی ViewModel مسئول برقراری تماس شبکه برای دریافت اطلاعات املاک و مستغلات مریخ است. جزئیات ViewModel جزئیات مربوط به تک تک املاک و مستغلات مریخ را که در قسمت جزئیات نمایش داده شده است، در خود نگه می دارد. برای هر ViewModel ، از LiveData با داده‌های مرتبط با چرخه حیات برای به‌روزرسانی رابط کاربری برنامه هنگام تغییر داده‌ها استفاده می‌کنید.

شما از مولفه Navigation هم برای پیمایش بین دو قطعه و هم برای ارسال ویژگی انتخاب شده به عنوان آرگومان استفاده می کنید.

در این کار، برنامه استارتر MarsRealEstate را دانلود و اجرا می کنید و با ساختار پروژه آشنا می شوید.

مرحله 1: قطعات و ناوبری را کاوش کنید

  1. برنامه شروع MarsRealEstate را دانلود کنید و آن را در Android Studio باز کنید.
  2. app/java/MainActivity.kt را بررسی کنید. این برنامه از قطعات برای هر دو صفحه استفاده می کند، بنابراین تنها وظیفه فعالیت بارگذاری طرح بندی فعالیت است.
  3. app/res/layout/activity_main.xml بررسی کنید. طرح فعالیت میزبان دو قطعه است که در فایل ناوبری تعریف شده است. این طرح‌بندی یک NavHostFragment و کنترل‌کننده ناوبری مرتبط با آن را با منبع nav_graph نمونه‌سازی می‌کند.
  4. app/res/navigation/nav_graph.xml باز کنید. در اینجا می توانید رابطه ناوبری بین دو قطعه را مشاهده کنید. گراف پیمایش StartDestination به overviewFragment اشاره می‌کند، بنابراین قطعه نمای کلی هنگام راه‌اندازی برنامه نمونه‌سازی می‌شود.

مرحله 2: فایل منبع Kotlin و اتصال داده ها را کاوش کنید

  1. در بخش Project ، برنامه > java را گسترش دهید. توجه داشته باشید که برنامه MarsRealEstate دارای سه پوشه بسته است: detail ، network و overview . اینها با سه مؤلفه اصلی برنامه شما مطابقت دارند: بخش های نمای کلی و جزئیات، و کد لایه شبکه.
  2. app/java/overview/OverviewFragment.kt باز کنید. OverviewFragment با تنبلی OverviewViewModel را مقداردهی اولیه می کند، به این معنی که OverviewViewModel اولین باری که استفاده می شود ایجاد می شود.
  3. متد onCreateView() را بررسی کنید. این روش طرح fragment_overview را با استفاده از binding داده‌ها افزایش می‌دهد، مالک چرخه حیات binding را روی خود تنظیم می‌کند ( this )، و متغیر viewModel در شی binding روی آن تنظیم می‌کند. از آنجایی که ما مالک چرخه حیات را تنظیم کرده‌ایم، هر LiveData استفاده شده در پیوند داده‌ها به‌طور خودکار برای هر گونه تغییر مشاهده می‌شود و رابط کاربری متناسب با آن به‌روزرسانی می‌شود.
  4. app/java/overview/OverviewViewModel باز کنید. از آنجا که پاسخ یک LiveData است و ما چرخه حیات را برای متغیر binding تنظیم کرده‌ایم، هر تغییری در آن باعث به‌روزرسانی رابط کاربری برنامه می‌شود.
  5. بلوک init را بررسی کنید. هنگامی که ViewModel ایجاد می شود، متد getMarsRealEstateProperties() را فراخوانی می کند.
  6. متد getMarsRealEstateProperties() بررسی کنید. در این برنامه شروع، این روش حاوی یک پاسخ نگهدارنده است. هدف این نرم افزار کد، به روز رسانی پاسخ LiveData در ViewModel با استفاده از داده های واقعی است که از اینترنت دریافت می کنید.
  7. app/res/layout/fragment_overview.xml باز کنید. این طرح بندی برای قطعه نمای کلی است که در این لبه کد با آن کار می کنید، و شامل اتصال داده برای مدل view است. OverviewViewModel را وارد می کند و سپس پاسخ را از ViewModel به TextView متصل می کند. در کدهای بعدی، نمای متن را با شبکه‌ای از تصاویر در RecyclerView جایگزین می‌کنید.
  8. برنامه را کامپایل و اجرا کنید. تنها چیزی که در نسخه فعلی این برنامه می‌بینید، پاسخ آغازگر است — "Set the Mars API Response here!"

داده های املاک و مستغلات مریخ به عنوان یک سرویس وب REST در یک وب سرور ذخیره می شود. وب سرویس ها از معماری REST استفاده می کنند که با استفاده از مولفه ها و پروتکل های استاندارد وب ساخته می شوند.

شما به روشی استاندارد از طریق URI به یک وب سرویس درخواست می دهید. URL آشنای وب در واقع نوعی از URI است و هر دو در طول این دوره به جای یکدیگر استفاده می شوند. به عنوان مثال، در برنامه این درس، تمام داده ها را از سرور زیر بازیابی می کنید:

https://android-kotlin-fun-mars-server.appspot.com

اگر URL زیر را در مرورگر خود تایپ کنید، لیستی از تمام املاک موجود در مریخ دریافت می کنید!

https://android-kotlin-fun-mars-server.appspot.com/realestate

پاسخ از یک وب سرویس معمولاً در JSON قالب بندی می شود، یک قالب تبادلی برای نمایش داده های ساخت یافته. در کار بعدی درباره JSON بیشتر می‌آموزید، اما توضیح کوتاه این است که یک شی JSON مجموعه‌ای از جفت‌های کلید-مقدار است که گاهی اوقات یک فرهنگ لغت ، نقشه هش یا یک آرایه انجمنی نامیده می‌شود. مجموعه ای از اشیاء JSON یک آرایه JSON است و آرایه ای است که به عنوان پاسخ از یک وب سرویس دریافت می کنید.

برای دریافت این داده ها به برنامه، برنامه شما باید یک اتصال شبکه برقرار کند و با آن سرور ارتباط برقرار کند، و سپس داده های پاسخ را در قالبی که برنامه می تواند استفاده کند، دریافت و تجزیه کند. در این کد لبه از یک کتابخانه کلاینت REST به نام Retrofit برای ایجاد این اتصال استفاده می کنید.

مرحله 1: وابستگی های Retrofit را به Gradle اضافه کنید

  1. build.gradle (ماژول: برنامه) را باز کنید.
  2. در بخش dependencies ، این خطوط را برای کتابخانه های Retrofit اضافه کنید:
implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"


توجه داشته باشید که شماره نسخه ها به طور جداگانه در فایل Gradle پروژه تعریف شده است. وابستگی اول برای خود کتابخانه Retrofit 2 و وابستگی دوم برای مبدل اسکالر Retrofit است. این مبدل Retrofit را قادر می‌سازد تا نتیجه JSON را به عنوان یک String برگرداند. این دو کتابخانه با هم کار می کنند.

  1. روی Sync Now کلیک کنید تا پروژه با وابستگی های جدید بازسازی شود.

مرحله 2: MarsApiService را پیاده سازی کنید

Retrofit یک API شبکه برای برنامه بر اساس محتوای وب سرویس ایجاد می کند. داده‌ها را از وب سرویس دریافت می‌کند و آن‌ها را از طریق یک کتابخانه مبدل جداگانه هدایت می‌کند که می‌داند چگونه داده‌ها را رمزگشایی کرده و آن‌ها را در قالب اشیاء مفید برگرداند. Retrofit شامل پشتیبانی داخلی از فرمت های داده وب محبوب مانند XML و JSON است. Retrofit در نهایت بیشتر لایه شبکه را برای شما ایجاد می کند، از جمله جزئیات مهم مانند اجرای درخواست ها در موضوعات پس زمینه.

کلاس MarsApiService لایه شبکه را برای برنامه نگه می دارد. یعنی این API است که ViewModel شما برای برقراری ارتباط با وب سرویس استفاده می کند. این کلاسی است که در آن API سرویس Retrofit را پیاده سازی خواهید کرد.

  1. app/java/network/MarsApiService.kt را باز کنید. در حال حاضر فایل فقط یک چیز دارد: یک ثابت برای URL پایه برای وب سرویس.
private const val BASE_URL = 
   "https://android-kotlin-fun-mars-server.appspot.com"
  1. درست زیر آن ثابت، از سازنده Retrofit برای ایجاد یک شی Retrofit استفاده کنید. در صورت درخواست retrofit2.Retrofit و retrofit2.converter.scalars.ScalarsConverterFactory را وارد کنید.
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())
   .baseUrl(BASE_URL)
   .build()

Retrofit برای ایجاد یک API خدمات وب حداقل به دو چیز در دسترس نیاز دارد: URI پایه برای وب سرویس و یک کارخانه مبدل. مبدل به Retrofit می گوید که با داده هایی که از وب سرویس دریافت می کند چه کند. در این مورد، می‌خواهید Retrofit یک پاسخ JSON را از وب سرویس دریافت کند و آن را به عنوان یک String برگرداند. Retrofit یک ScalarsConverter دارد که از رشته‌ها و دیگر انواع اولیه پشتیبانی می‌کند، بنابراین می‌توانید addConverterFactory() در سازنده با نمونه‌ای از ScalarsConverterFactory فراخوانی کنید. در نهایت، build() برای ایجاد شی Retrofit فراخوانی می کنید.

  1. درست در زیر تماس با سازنده Retrofit، رابطی را تعریف کنید که نحوه گفتگوی Retrofit با سرور وب با استفاده از درخواست های HTTP را تعریف کند. در صورت درخواست retrofit2.http.GET و retrofit2.Call را وارد کنید.
interface MarsApiService {
    @GET("realestate")
    fun getProperties():
            Call<String>
}

در حال حاضر هدف دریافت رشته پاسخ JSON از وب سرویس است و برای انجام آن فقط به یک روش نیاز دارید: getProperties() . برای اینکه به Retrofit بگویید این روش چه کاری باید انجام دهد، از یک حاشیه نویسی @GET استفاده کنید و مسیر یا نقطه پایانی را برای آن روش وب سرویس مشخص کنید. در این مورد نقطه پایانی realestate نامیده می شود. هنگامی که متد getProperties() فراخوانی می شود، Retrofit realestate نقطه پایانی را به URL پایه (که در سازنده Retrofit تعریف کرده اید) اضافه می کند و یک آبجکت Call ایجاد می کند. آن شی Call برای شروع درخواست استفاده می شود.

  1. در زیر رابط MarsApiService ، یک شیء عمومی به نام MarsApi برای مقداردهی اولیه سرویس Retrofit تعریف کنید.
object MarsApi {
    val retrofitService : MarsApiService by lazy { 
       retrofit.create(MarsApiService::class.java) }
}

متد Retrofit create() خود سرویس Retrofit را با رابط MarsApiService ایجاد می کند. از آنجایی که این تماس گران است و برنامه فقط به یک نمونه سرویس Retrofit نیاز دارد، شما با استفاده از یک شی عمومی به نام MarsApi ، سرویس را در معرض بقیه برنامه قرار می‌دهید و با تنبلی سرویس Retrofit را در آنجا مقداردهی اولیه می‌کنید. اکنون که تمام تنظیمات انجام شده است، هر بار که برنامه شما با MarsApi.retrofitService تماس می گیرد، یک شی Retrofit تک تنی دریافت می کند که MarsApiService پیاده سازی می کند.

مرحله 3: با سرویس وب در OverviewViewModel تماس بگیرید

  1. app/java/overview/OverviewViewModel.kt باز کنید. به سمت پایین به متد getMarsRealEstateProperties() بروید.
private fun getMarsRealEstateProperties() {
   _response.value = "Set the Mars API Response here!"
}

این روشی است که در آن سرویس Retrofit را فراخوانی کرده و رشته JSON برگشتی را مدیریت می‌کنید. در حال حاضر فقط یک رشته نگهدارنده برای پاسخ وجود دارد.

  1. خط متغیری را که پاسخ را روی "Set the Mars API Response here!"
  2. داخل getMarsRealEstateProperties() کد زیر را اضافه کنید. در صورت درخواست retrofit2.Callback و com.example.android.marsrealestate.network.MarsApi را وارد کنید.

    متد MarsApi.retrofitService.getProperties() یک شی Call را برمی گرداند. سپس می‌توانید enqueue() روی آن شیء فراخوانی کنید تا درخواست شبکه در یک رشته پس‌زمینه شروع شود.
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<String> {
})
  1. روی کلمه object که زیر آن با رنگ قرمز مشخص شده است کلیک کنید. کد > پیاده سازی روش ها را انتخاب کنید. هر دو onResponse() و onFailure() از لیست انتخاب کنید.


    Android Studio کد را با TODO در هر روش اضافه می کند:
override fun onFailure(call: Call<String>, t: Throwable) {
       TODO("not implemented") 
}

override fun onResponse(call: Call<String>, 
   response: Response<String>) {
       TODO("not implemented") 
}
  1. در onFailure() TODO را حذف کنید و _response مانند شکل زیر روی یک پیام شکست تنظیم کنید. _response یک رشته LiveData است که تعیین می کند چه چیزی در نمای متن نشان داده می شود. هر ایالت باید _response LiveData به روز کند.

    هنگامی که پاسخ سرویس وب با شکست مواجه می شود، فراخوانی onFailure() فراخوانی می شود. برای این پاسخ، وضعیت _response را روی "Failure: " تنظیم کنید که با پیام آرگومان Throwable الحاق شده است.
override fun onFailure(call: Call<String>, t: Throwable) {
   _response.value = "Failure: " + t.message
}
  1. در onResponse() TODO را حذف کرده و _response روی بدنه پاسخ تنظیم کنید. زمانی که درخواست موفقیت آمیز باشد و وب سرویس پاسخی را برمی گرداند، onResponse() فراخوانی می شود.
override fun onResponse(call: Call<String>, 
   response: Response<String>) {
      _response.value = response.body()
}

مرحله 4: مجوز اینترنت را تعریف کنید

  1. برنامه MarsRealEstate را کامپایل و اجرا کنید. توجه داشته باشید که برنامه بلافاصله با یک خطا بسته می شود.
  2. روی زبانه Logcat در اندروید استودیو کلیک کنید و خطای گزارش را یادداشت کنید که با خطی مانند این شروع می‌شود:
Process: com.example.android.marsrealestate, PID: 10646
java.lang.SecurityException: Permission denied (missing INTERNET permission?)

پیام خطا به شما می گوید که برنامه شما ممکن است مجوز INTERNET را نداشته باشد. اتصال به اینترنت نگرانی های امنیتی ایجاد می کند، به همین دلیل است که برنامه ها به طور پیش فرض اتصال اینترنت ندارند. شما باید به صراحت به اندروید بگویید که برنامه نیاز به دسترسی به اینترنت دارد.

  1. app/manifests/AndroidManifest.xml باز کنید. این خط را درست قبل از تگ <application> اضافه کنید:
<uses-permission android:name="android.permission.INTERNET" />
  1. برنامه را کامپایل و دوباره اجرا کنید. اگر همه چیز با اتصال اینترنت شما درست کار می کند، متن JSON حاوی داده های Mars Property را مشاهده می کنید.
  2. روی دکمه برگشت در دستگاه یا شبیه ساز خود ضربه بزنید تا برنامه بسته شود.
  3. دستگاه یا شبیه ساز خود را در حالت هواپیما قرار دهید و سپس برنامه را از منوی Recents دوباره باز کنید یا برنامه را از Android Studio ریستارت کنید.


  1. دوباره حالت هواپیما را خاموش کنید.

اکنون یک پاسخ JSON از وب سرویس Mars دریافت می کنید که شروعی عالی است. اما چیزی که واقعاً به آن نیاز دارید، اشیاء Kotlin هستند، نه یک رشته بزرگ JSON. کتابخانه ای به نام Moshi وجود دارد که یک تجزیه کننده JSON Android است که یک رشته JSON را به اشیاء Kotlin تبدیل می کند. Retrofit یک مبدل دارد که با Moshi کار می کند، بنابراین یک کتابخانه عالی برای اهداف شما در اینجا است.

در این کار، شما از کتابخانه Moshi با Retrofit برای تجزیه پاسخ JSON از وب سرویس به اشیاء مفید Mars Property Kotlin استفاده می کنید. برنامه را طوری تغییر می‌دهید که به جای نمایش JSON خام، برنامه تعداد ویژگی‌های Mars بازگشتی را نمایش دهد.

مرحله 1: وابستگی های کتابخانه Moshi را اضافه کنید

  1. build.gradle (ماژول: برنامه) را باز کنید.
  2. در بخش وابستگی ها، کد زیر را اضافه کنید تا وابستگی های Moshi را نیز شامل شود. همانند Retrofit، $version_moshi به طور جداگانه در فایل Gradle در سطح پروژه تعریف شده است. این وابستگی ها پشتیبانی از کتابخانه اصلی Moshi JSON و پشتیبانی از Moshi's Kotlin را اضافه می کنند.
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
  1. خط مبدل اسکالر Retrofit را در بلوک dependencies پیدا کنید:
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"
  1. آن خط را برای استفاده از converter-moshi تغییر دهید:
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
  1. روی Sync Now کلیک کنید تا پروژه با وابستگی های جدید بازسازی شود.

مرحله 2: کلاس داده MarsProperty را پیاده سازی کنید

یک ورودی نمونه از پاسخ JSON که از وب سرویس دریافت می کنید چیزی شبیه به این است:

[{"price":450000,
"id":"424906",
"type":"rent",
"img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"},
...]

پاسخ JSON نشان داده شده در بالا یک آرایه است که با براکت های مربع نشان داده می شود. این آرایه حاوی اشیاء JSON است که توسط پرانتزهای فرفری احاطه شده اند. هر شی شامل مجموعه ای از جفت های نام-مقدار است که با دو نقطه از هم جدا شده اند. نام ها با نقل قول احاطه شده اند. مقادیر می توانند اعداد یا رشته ها باشند و رشته ها نیز با نقل قول احاطه شده اند. به عنوان مثال، price این ویژگی 450000 دلار است و img_src یک URL است که محل فایل تصویر در سرور است.

در مثال بالا، توجه کنید که هر ورودی ویژگی Mars دارای این جفت‌های کلید و مقادیر JSON است:

  • price : قیمت ملک مریخ، به عنوان یک عدد.
  • id : شناسه اموال، به عنوان یک رشته.
  • type : یا "rent" یا "buy" .
  • img_src : URL تصویر به عنوان یک رشته.

موشی این داده های JSON را تجزیه و به اشیاء Kotlin تبدیل می کند. برای انجام این کار، باید یک کلاس داده Kotlin برای ذخیره نتایج تجزیه شده داشته باشد، بنابراین مرحله بعدی ایجاد آن کلاس است.

  1. app/java/network/MarsProperty.kt را باز کنید.
  2. تعریف کلاس MarsProperty موجود را با کد زیر جایگزین کنید:
data class MarsProperty(
   val id: String, val img_src: String,
   val type: String,
   val price: Double
)

توجه داشته باشید که هر یک از متغیرهای کلاس MarsProperty با یک نام کلید در شی JSON مطابقت دارد. برای تطبیق انواع در JSON، از اشیاء String برای همه مقادیر استفاده می‌کنید به جز price ، که یک Double است. یک Double می تواند برای نمایش هر شماره JSON استفاده شود.

هنگامی که Moshi JSON را تجزیه می کند، کلیدها را با نام مطابقت می دهد و اشیاء داده را با مقادیر مناسب پر می کند.

  1. خط کلید img_src را با خط زیر جایگزین کنید. com.squareup.moshi.Json در صورت درخواست وارد کنید.
@Json(name = "img_src") val imgSrcUrl: String,

گاهی اوقات نام‌های کلید در پاسخ JSON می‌توانند ویژگی‌های Kotlin گیج‌کننده ایجاد کنند، یا ممکن است با سبک کدنویسی شما مطابقت نداشته باشند - به عنوان مثال، در فایل JSON، کلید img_src از زیرخط استفاده می‌کند، در حالی که ویژگی‌های Kotlin معمولاً از حروف بزرگ و کوچک استفاده می‌کنند ("camel case").

برای استفاده از نام متغیرها در کلاس داده خود که با نام کلید در پاسخ JSON متفاوت است، از حاشیه نویسی @Json استفاده کنید. در این مثال، نام متغیر در کلاس داده imgSrcUrl است. متغیر با استفاده از @Json(name = "img_src") به ویژگی JSON img_src نگاشت می شود.

مرحله 3: MarsApiService و OverviewViewModel را به روز کنید

با وجود کلاس داده MarsProperty ، اکنون می‌توانید API شبکه و ViewModel را به‌روزرسانی کنید تا داده‌های Moshi را نیز شامل شود.

  1. network/MarsApiService.kt باز کنید. ممکن است خطاهای کلاس گمشده را برای ScalarsConverterFactory ببینید. این به دلیل تغییر وابستگی Retrofit است که در مرحله 1 ایجاد کردید. شما آن خطاها را به زودی برطرف می کنید.
  2. در بالای فایل، درست قبل از سازنده Retrofit، کد زیر را برای ایجاد نمونه Moshi اضافه کنید. در صورت درخواست com.squareup.moshi.Moshi و com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory را وارد کنید.
private val moshi = Moshi.Builder()
   .add(KotlinJsonAdapterFactory())
   .build()

مشابه کاری که با Retrofit انجام دادید، در اینجا با استفاده از سازنده Moshi یک شی moshi ایجاد می کنید. برای اینکه یادداشت‌های Moshi به درستی با Kotlin کار کنند، KotlinJsonAdapterFactory را اضافه کنید و سپس build() را فراخوانی کنید.

  1. سازنده Retrofit را تغییر دهید تا از MoshiConverterFactory به جای ScalarConverterFactory استفاده کند و نمونه moshi را که ایجاد کرده‌اید ارسال کنید. در صورت درخواست retrofit2.converter.moshi.MoshiConverterFactory را وارد کنید.
private val retrofit = Retrofit.Builder()
   .addConverterFactory(MoshiConverterFactory.create(moshi))
   .baseUrl(BASE_URL)
   .build()
  1. واردات ScalarConverterFactory را نیز حذف کنید.

کد برای حذف:

import retrofit2.converter.scalars.ScalarsConverterFactory
  1. رابط MarsApiService را به روز کنید تا Retrofit لیستی از اشیاء MarsProperty را به جای بازگرداندن Call<String> برگرداند.
interface MarsApiService {
   @GET("realestate")
   fun getProperties():
      Call<List<MarsProperty>>
}
  1. OverviewViewModel.kt باز کنید. به فراخوانی getProperties().enqueue() در متد getMarsRealEstateProperties() بروید.
  2. آرگومان را به enqueue() از Callback<String> به Callback<List<MarsProperty>> تغییر دهید. در صورت درخواست، com.example.android.marsrealestate.network.MarsProperty را وارد کنید.
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<List<MarsProperty>> {
  1. در onFailure() آرگومان را از Call<String> به Call<List<MarsProperty>> تغییر دهید:
override fun onFailure(call: Call<List<MarsProperty>>, t: Throwable) {
  1. همان تغییر را در هر دو آرگومان onResponse() ایجاد کنید:
override fun onResponse(call: Call<List<MarsProperty>>, 
   response: Response<List<MarsProperty>>) {
  1. در بدنه onResponse() انتساب موجود را به _response.value با انتساب زیر جایگزین کنید. از آنجا که response.body() اکنون لیستی از اشیاء MarsProperty است، اندازه آن لیست تعداد خواصی است که تجزیه شده اند. این پیام پاسخ این تعداد ویژگی را چاپ می کند:
_response.value = 
   "Success: ${response.body()?.size} Mars properties retrieved"
  1. مطمئن شوید که حالت هواپیما خاموش است. برنامه را کامپایل و اجرا کنید. این بار پیام باید تعداد ویژگی های بازگشتی از وب سرویس را نشان دهد:

اکنون سرویس Retrofit API در حال اجرا است، اما از یک callback با دو روش برگشتی استفاده می‌کند که شما مجبور بودید پیاده‌سازی کنید. یک روش موفقیت را مدیریت می کند و دیگری شکست را مدیریت می کند و نتیجه شکست استثناها را گزارش می دهد. اگر می‌توانید به جای استفاده از تماس‌های برگشتی، از کوروتین‌ها با مدیریت استثنا استفاده کنید، کد شما کارآمدتر و خواندن آسان‌تر خواهد بود. به‌راحتی، Retrofit کتابخانه‌ای دارد که کوروتین‌ها را ادغام می‌کند.

در این کار، سرویس شبکه خود و ViewModel را به استفاده از کوروتین ها تبدیل می کنید.

مرحله 1: وابستگی های کوروتین را اضافه کنید

  1. build.gradle (ماژول: برنامه) را باز کنید.
  2. در بخش وابستگی ها، پشتیبانی از کتابخانه های اصلی Kotlin و کتابخانه Coroutine Retrofit را اضافه کنید:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines"

implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$version_retrofit_coroutines_adapter"
  1. روی Sync Now کلیک کنید تا پروژه با وابستگی های جدید بازسازی شود.

مرحله 2: MarsApiService و OverviewViewModel را به روز کنید

  1. در MarsApiService.kt ، سازنده Retrofit را برای استفاده از CoroutineCallAdapterFactory به روز کنید. سازنده کامل اکنون به شکل زیر است:
private val retrofit = Retrofit.Builder()
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .addCallAdapterFactory(CoroutineCallAdapterFactory())
        .baseUrl(BASE_URL)
        .build()

آداپتورهای تماس، توانایی Retrofit را برای ایجاد APIهایی که چیزی غیر از کلاس Call پیش‌فرض را برمی‌گردانند، اضافه می‌کنند. در این مورد، CoroutineCallAdapterFactory به ما اجازه می دهد تا شی Call را که getProperties() برمی گرداند، با یک شی Deferred جایگزین کنیم.

  1. در متد getProperties() Call<List<MarsProperty>> به Deferred<List<MarsProperty>> تغییر دهید. در صورت درخواست kotlinx.coroutines.Deferred وارد کنید. متد کامل getProperties() به شکل زیر است:
@GET("realestate")
fun getProperties():
   Deferred<List<MarsProperty>>

رابط Deferred یک کار معمولی را تعریف می کند که یک مقدار نتیجه را برمی گرداند ( Deferred از Job ارث می برد). رابط Deferred شامل متدی به نام await() است که باعث می‌شود کد شما بدون مسدود کردن منتظر بماند تا مقدار آماده شود و سپس آن مقدار برگردانده شود.

  1. OverviewViewModel.kt باز کنید. درست قبل از بلوک init ، یک کار معمولی اضافه کنید:
private var viewModelJob = Job()
  1. با استفاده از توزیع کننده اصلی، یک محدوده کاری برای آن کار جدید ایجاد کنید:
private val coroutineScope = CoroutineScope(
   viewModelJob + Dispatchers.Main )

توزیع کننده Dispatchers.Main از رشته UI برای کار خود استفاده می کند. از آنجایی که Retrofit تمام کار خود را روی یک رشته پس‌زمینه انجام می‌دهد، دلیلی برای استفاده از رشته‌های دیگر برای دامنه وجود ندارد. این به شما این امکان را می دهد که به راحتی مقدار MutableLiveData را هنگامی که به نتیجه می رسید به روز کنید.

  1. تمام کدهای داخل getMarsRealEstateProperties() را حذف کنید. شما در اینجا به جای فراخوانی برای enqueue() و پاسخ تماس‌های onFailure() و onResponse() در اینجا از coroutine استفاده خواهید کرد.
  2. در داخل getMarsRealEstateProperties() ، coroutine را اجرا کنید:
coroutineScope.launch { 

}


برای استفاده از آبجکت Deferred که Retrofit برای کار شبکه برمی گرداند، باید درون یک کوروتین باشید، بنابراین در اینجا کوروتینی را که ایجاد کرده اید اجرا می کنید. شما همچنان در حال اجرای کد در رشته اصلی هستید، اما اکنون به کوروتین ها اجازه مدیریت همزمانی را می دهید.

  1. در داخل بلوک راه اندازی، getProperties() در شی retrofitService فراخوانی کنید:
var getPropertiesDeferred = MarsApi.retrofitService.getProperties()

فراخوانی getProperties() از سرویس MarsApi تماس شبکه را روی یک رشته پس‌زمینه ایجاد و شروع می‌کند و شی Deferred را برای آن کار برمی‌گرداند.

  1. همچنین در داخل بلوک راه اندازی، یک بلوک try / catch برای رسیدگی به استثناها اضافه کنید:
try {

} catch (e: Exception) {
  
}
  1. در داخل بلوک try {} ، await() را در شی Deferred فراخوانی کنید:
var listResult = getPropertiesDeferred.await()

فراخوانی await() روی شی Deferred نتیجه تماس شبکه را زمانی که مقدار آماده است برمی گرداند. متد await() غیر مسدود است، بنابراین سرویس Mars API داده‌ها را از شبکه بدون مسدود کردن رشته فعلی بازیابی می‌کند – که این مهم است زیرا ما در محدوده رشته رابط کاربری هستیم. پس از اتمام کار، کد شما از همان جایی که متوقف شد به اجرا ادامه می دهد. این در داخل try {} است تا بتوانید استثناها را بگیرید.

  1. همچنین در داخل بلوک try {} ، پس از متد await() پیام پاسخ را برای پاسخ موفقیت آمیز به روز کنید:
_response.value = 
   "Success: ${listResult.size} Mars properties retrieved"
  1. در داخل بلوک catch {} ، پاسخ شکست را مدیریت کنید:
_response.value = "Failure: ${e.message}"


متد کامل getMarsRealEstateProperties() اکنون به شکل زیر است:

private fun getMarsRealEstateProperties() {
   coroutineScope.launch {
       var getPropertiesDeferred = 
          MarsApi.retrofitService.getProperties()
       try {          
           _response.value = 
              "Success: ${listResult.size} Mars properties retrieved"
       } catch (e: Exception) {
           _response.value = "Failure: ${e.message}"
       }
   }
}
  1. در انتهای کلاس، یک callback onCleared() با این کد اضافه کنید:
override fun onCleared() {
   super.onCleared()
   viewModelJob.cancel()
}

وقتی ViewModel از بین می‌رود، بارگیری داده‌ها باید متوقف شود، زیرا OverviewFragment که از این ViewModel استفاده می‌کند از بین خواهد رفت. برای توقف بارگیری زمانی که ViewModel از بین می رود، onCleared() لغو می کنید تا کار لغو شود.

  1. برنامه را کامپایل و اجرا کنید. شما این بار همان نتیجه را در کار قبلی دریافت می کنید (گزارشی از تعداد ویژگی ها)، اما با کد ساده تر و مدیریت خطا.

پروژه اندروید استودیو: MarsRealEstateNetwork

خدمات وب REST

  • وب سرویس سرویسی در اینترنت است که به برنامه شما امکان می دهد درخواست ها را ارسال کند و داده ها را پس بگیرد.
  • وب سرویس های رایج از معماری REST استفاده می کنند. سرویس‌های وب که معماری REST را ارائه می‌دهند به عنوان سرویس‌های RESTful شناخته می‌شوند. خدمات وب RESTful با استفاده از اجزا و پروتکل های وب استاندارد ساخته می شوند.
  • شما از طریق URI به یک سرویس وب REST به صورت استاندارد درخواست می کنید.
  • برای استفاده از وب سرویس، یک برنامه باید یک اتصال شبکه برقرار کرده و با سرویس ارتباط برقرار کند. سپس برنامه باید داده های پاسخ را در قالبی که برنامه می تواند استفاده کند، دریافت و تجزیه کند.
  • کتابخانه Retrofit یک کتابخانه سرویس گیرنده است که به برنامه شما امکان می دهد تا به یک سرویس وب REST درخواست دهد.
  • از مبدل‌ها استفاده کنید تا به Retrofit بگویید با داده‌هایی که به وب سرویس می‌فرستد و از وب سرویس برمی‌گردد چه کند. به عنوان مثال، مبدل ScalarsConverter با داده های وب سرویس به عنوان یک String یا دیگر اولیه رفتار می کند.
  • برای فعال کردن برنامه خود برای برقراری اتصال به اینترنت، مجوز "android.permission.INTERNET" را در مانیفست Android اضافه کنید.

تجزیه JSON

  • پاسخ از یک وب سرویس اغلب در JSON قالب بندی می شود، یک قالب تبادل متداول برای نمایش داده های ساخت یافته.
  • یک شی JSON مجموعه ای از جفت های کلید-مقدار است. این مجموعه گاهی اوقات یک فرهنگ لغت ، نقشه هش یا آرایه انجمنی نامیده می شود.
  • مجموعه ای از اشیاء JSON یک آرایه JSON است. شما یک آرایه JSON را به عنوان پاسخ از یک وب سرویس دریافت می کنید.
  • کلیدهای یک جفت کلید-مقدار با نقل قول احاطه شده اند. مقادیر می توانند عدد یا رشته باشند. رشته ها نیز با نقل قول احاطه شده اند.
  • کتابخانه Moshi تجزیه کننده JSON Android است که یک رشته JSON را به اشیاء Kotlin تبدیل می کند. Retrofit یک مبدل دارد که با Moshi کار می کند.
  • Moshi کلیدهای یک پاسخ JSON را با ویژگی‌های موجود در یک شی داده که نام یکسانی دارند، مطابقت می‌دهد.
  • برای استفاده از نام ویژگی متفاوت برای یک کلید، آن ویژگی را با حاشیه نویسی @Json و نام کلید JSON حاشیه نویسی کنید.

بازسازی و کوروتین ها

  • آداپتورهای تماس به Retrofit اجازه می دهند API هایی ایجاد کند که چیزی غیر از کلاس Call پیش فرض را برمی گرداند. از کلاس CoroutineCallAdapterFactory برای جایگزینی Call با یک coroutine Deferred استفاده کنید.
  • از متد await() در شی Deferred استفاده کنید تا باعث شود که کد اصلی شما بدون مسدود کردن منتظر بماند تا مقدار آماده شود و سپس مقدار برگردانده شود.

دوره بی ادبی:

مستندات توسعه دهنده اندروید:

اسناد کاتلین:

دیگر:

این بخش، تکالیف احتمالی را برای دانش‌آموزانی که در این آزمایشگاه کد به عنوان بخشی از دوره‌ای که توسط یک مربی هدایت می‌شود، کار می‌کنند، فهرست می‌کند. این وظیفه مربی است که موارد زیر را انجام دهد:

  • در صورت نیاز تکالیف را تعیین کنید.
  • نحوه ارسال تکالیف را با دانش آموزان در میان بگذارید.
  • تکالیف را نمره دهید.

مربیان می‌توانند از این پیشنهادات به اندازه‌ای که می‌خواهند استفاده کنند، و باید با خیال راحت هر تکلیف دیگری را که فکر می‌کنند مناسب است، محول کنند.

اگر به تنهایی از طریق این کدها کار می کنید، از این تکالیف برای آزمایش دانش خود استفاده کنید.

به این سوالات پاسخ دهید

سوال 1

دو مورد کلیدی که Retrofit برای ساخت API خدمات وب نیاز دارد چیست؟

▢ URI پایه برای وب سرویس و یک جستار GET .

▢ URI پایه برای وب سرویس و یک کارخانه مبدل.

▢ یک اتصال شبکه به سرویس وب و یک نشانه مجوز.

▢ یک کارخانه مبدل و یک تجزیه کننده برای پاسخ.

سوال 2

هدف از تاسیس کتابخانه موشی چیست؟

▢ برای بازگرداندن داده ها از یک سرویس وب.

▢ برای تعامل با Retrofit برای درخواست خدمات وب.

▢ برای تجزیه پاسخ JSON از یک وب سرویس به اشیاء داده Kotlin.

▢ برای تغییر نام اشیاء Kotlin برای مطابقت با کلیدهای موجود در پاسخ JSON.

سوال 3

آداپتورهای تماس Retrofit برای چه مواردی استفاده می شوند؟

▢ آنها Retrofit را قادر می سازند تا از کوروتین ها استفاده کند.

▢ آنها پاسخ سرویس وب را با اشیاء داده Kotlin تطبیق می دهند.

▢ آنها یک تماس Retrofit را به تماس سرویس وب تغییر می دهند.

▢ آنها توانایی برگرداندن چیزی غیر از کلاس Call پیش فرض را در Retrofit اضافه می کنند.

درس بعدی را شروع کنید: 8.2 بارگیری و نمایش تصاویر از اینترنت

برای پیوند به سایر کدهای این دوره، به صفحه فرود کد لبه‌های کد پایه Android Kotlin Fundamentals مراجعه کنید.