Dasar-Dasar Android Kotlin 08.1: Mendapatkan data dari internet

Codelab ini adalah bagian dari kursus Dasar-Dasar Android Kotlin. Anda akan mendapatkan manfaat maksimal dari kursus ini jika menyelesaikan codelab secara berurutan. Semua codelab kursus tercantum di halaman landing codelab Dasar-Dasar Android Kotlin.

Pengantar

Hampir semua aplikasi Android yang Anda buat akan perlu terhubung ke internet pada suatu saat. Dalam codelab ini dan codelab berikutnya, Anda akan membuat aplikasi yang terhubung ke layanan web untuk mengambil dan menampilkan data. Anda juga akan membangun apa yang telah Anda pelajari di codelab sebelumnya tentang ViewModel, LiveData, dan RecyclerView.

Dalam codelab ini, Anda menggunakan library yang dikembangkan komunitas untuk membuat lapisan jaringan. Hal ini sangat menyederhanakan pengambilan data dan gambar, serta membantu aplikasi agar sesuai dengan beberapa praktik terbaik Android, seperti memuat gambar di thread latar belakang dan menyimpan gambar yang dimuat dalam cache. Untuk bagian asinkron atau non-blocking dalam kode, seperti berkomunikasi dengan lapisan layanan web, Anda akan mengubah aplikasi untuk menggunakan coroutine Kotlin. Anda juga akan mengupdate antarmuka pengguna aplikasi jika internet lambat atau tidak tersedia untuk memberi tahu pengguna apa yang sedang terjadi.

Yang harus sudah Anda ketahui

  • Cara membuat dan menggunakan fragmen.
  • Cara beralih antar-fragmen, dan menggunakan safeArgs untuk meneruskan data antar-fragmen.
  • Cara menggunakan komponen arsitektur termasuk transformasi ViewModel, ViewModelProvider.Factory, LiveData, dan LiveData.
  • Cara menggunakan coroutine untuk tugas yang berjalan lama.

Yang akan Anda pelajari

  • Apa itu layanan web REST.
  • Menggunakan library Retrofit untuk terhubung ke layanan web REST di internet dan mendapatkan respons.
  • Menggunakan library Moshi untuk mengurai respons JSON ke dalam objek data.

Yang akan Anda lakukan

  • Memodifikasi aplikasi awal untuk membuat permintaan API layanan web dan menangani respons.
  • Terapkan lapisan jaringan untuk aplikasi Anda menggunakan library Retrofit.
  • Uraikan respons JSON dari layanan web ke data aktif aplikasi Anda dengan library Moshi.
  • Gunakan dukungan Retrofit untuk coroutine agar menyederhanakan kode.

Dalam codelab ini (dan codelab berikutnya), Anda akan menggunakan aplikasi starter bernama MarsRealEstate, yang menampilkan properti yang dijual di Mars. Aplikasi ini terhubung ke layanan web untuk mengambil dan menampilkan data properti, termasuk detail seperti harga dan apakah properti tersedia untuk dijual atau disewakan. Gambar yang merepresentasikan setiap properti adalah foto kehidupan nyata dari Mars yang diambil dari penjelajah Mars NASA.

Versi aplikasi yang Anda buat di codelab ini tidak akan memiliki banyak flash visual: berfokus pada bagian lapisan jaringan dari aplikasi untuk terhubung ke internet dan mendownload data properti mentah menggunakan layanan web. Untuk memastikan bahwa data diambil dan diuraikan dengan benar, Anda hanya perlu mencetak jumlah properti di Mars dalam tampilan teks:

.

Arsitektur untuk aplikasi MarsRealEstate memiliki dua modul utama:

  • Fragmen ringkasan, yang berisi petak gambar properti thumbnail, yang dibuat dengan RecyclerView.
  • Fragmen tampilan detail, yang berisi informasi tentang setiap properti.

Aplikasi memiliki ViewModel untuk setiap fragmen. Untuk codelab ini, Anda membuat lapisan untuk layanan jaringan, dan ViewModel berkomunikasi langsung dengan lapisan jaringan tersebut. Hal ini mirip dengan yang Anda lakukan di codelab sebelumnya saat ViewModel berkomunikasi dengan database Room.

ViewModel ringkasan bertanggung jawab untuk membuat panggilan jaringan guna mendapatkan informasi properti Mars. Detail ViewModel menyimpan detail untuk satu bagian lahan di Mars yang ditampilkan dalam fragmen detail. Untuk setiap ViewModel, Anda menggunakan LiveData dengan data binding yang memperhatikan siklus proses untuk mengupdate UI aplikasi saat data berubah.

Anda menggunakan komponen Navigation untuk menavigasi antara dua fragmen, dan untuk meneruskan properti yang dipilih sebagai argumen.

Dalam tugas ini, Anda akan mendownload dan menjalankan aplikasi awal untuk MarsRealEstate serta memahami struktur project.

Langkah 1: Jelajahi fragmen dan navigasi

  1. Download aplikasi awal MarsRealEstate dan buka di Android Studio.
  2. Periksa app/java/MainActivity.kt. Aplikasi menggunakan fragmen untuk kedua layar, sehingga satu-satunya tugas untuk aktivitas adalah memuat tata letak aktivitas.
  3. Periksa app/res/layout/activity_main.xml. Tata letak aktivitas adalah host untuk dua fragmen, yang ditentukan dalam file navigasi. Tata letak ini membuat instance NavHostFragment dan pengontrol navigasi terkaitnya dengan resource nav_graph.
  4. Buka app/res/navigation/nav_graph.xml. Di sini Anda dapat melihat hubungan navigasi antara kedua fragmen. Grafik navigasi StartDestination mengarah ke overviewFragment, sehingga fragmen ringkasan dibuat instance-nya saat aplikasi diluncurkan.

Langkah 2: Jelajahi file sumber Kotlindan data binding

  1. Di panel Project, luaskan app > java. Perhatikan bahwa aplikasi MarsRealEstate memiliki tiga folder paket: detail, network, dan overview. Ini sesuai dengan tiga komponen utama aplikasi Anda: ringkasan dan fragmen detail, serta kode untuk lapisan jaringan.
  2. Buka app/java/overview/OverviewFragment.kt. OverviewFragment melakukan inisialisasi OverviewViewModel secara lambat, yang berarti OverviewViewModel dibuat saat pertama kali digunakan.
  3. Periksa metode onCreateView(). Metode ini meng-inflate tata letak fragment_overview menggunakan data binding, menetapkan pemilik siklus proses binding ke dirinya sendiri (this), dan menetapkan variabel viewModel dalam objek binding ke pemilik tersebut. Karena kita telah menetapkan pemilik siklus proses, setiap LiveData yang digunakan dalam data binding akan otomatis diamati jika ada perubahan, dan UI akan diupdate sebagaimana mestinya.
  4. Buka app/java/overview/OverviewViewModel. Karena responsnya adalah LiveData dan kita telah menetapkan siklus proses untuk variabel binding, setiap perubahannya akan memperbarui UI aplikasi.
  5. Periksa blok init. Saat ViewModel dibuat, metode getMarsRealEstateProperties() akan dipanggil.
  6. Periksa metode getMarsRealEstateProperties(). Di aplikasi awal ini, metode ini berisi respons placeholder. Tujuan codelab ini adalah memperbarui LiveData respons dalam ViewModel menggunakan data nyata yang Anda dapatkan dari internet.
  7. Buka app/res/layout/fragment_overview.xml. Tata letak ini adalah tata letak untuk fragmen ringkasan yang Anda kerjakan dalam codelab ini, dan mencakup data binding untuk model tampilan. Tata letak ini mengimpor OverviewViewModel, lalu mengikat respons dari ViewModel ke TextView. Di codelab berikutnya, Anda akan mengganti tampilan teks dengan petak gambar di RecyclerView.
  8. Kompilasi dan jalankan aplikasi. Yang Anda lihat di aplikasi versi saat ini hanyalah respons awal—"Setel Respons Mars API di sini!"

Data real estate Mars disimpan di server web, sebagai layanan web REST. Layanan web yang menggunakan arsitektur REST dibuat menggunakan komponen dan protokol web standar.

Anda membuat permintaan ke layanan web dengan cara standar melalui URI. URL web yang dikenal sebenarnya adalah jenis URI, dan keduanya digunakan secara bergantian sepanjang kursus ini. Misalnya, dalam aplikasi untuk pelajaran ini, Anda akan mengambil semua data dari server berikut:

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

Jika Anda mengetik URL berikut di browser, Anda akan mendapatkan daftar semua properti real estate yang tersedia di Mars.

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

Respons dari layanan web biasanya diformat dalam JSON, format pertukaran untuk merepresentasikan data terstruktur. Anda akan mempelajari JSON lebih lanjut di tugas berikutnya, tetapi penjelasan singkatnya adalah bahwa objek JSON adalah kumpulan pasangan nilai-kunci, terkadang disebut kamus, peta hash, atau array asosiatif. Kumpulan objek JSON adalah array JSON, dan array ini adalah array yang Anda dapatkan kembali sebagai respons dari layanan web.

Untuk memasukkan data ini ke dalam aplikasi, aplikasi Anda harus membuat koneksi jaringan dan berkomunikasi dengan server tersebut, lalu menerima dan mengurai data respons ke dalam format yang dapat digunakan aplikasi. Dalam codelab ini, Anda menggunakan library klien REST yang disebut Retrofit untuk membuat koneksi ini.

Langkah 1: Tambahkan dependensi Retrofit ke Gradle

  1. Buka build.gradle (Module: app).
  2. Di bagian dependencies, tambahkan baris berikut untuk library Retrofit:
implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"


Perhatikan bahwa nomor versi ditentukan secara terpisah dalam file Gradle project. Dependensi pertama adalah untuk library Retrofit 2, dan dependensi kedua adalah untuk pengonversi skalar Retrofit. Pengonversi ini memungkinkan Retrofit menampilkan hasil JSON sebagai String. Kedua library bekerja bersama.

  1. Klik Sync Now untuk mem-build ulang project dengan dependensi baru.

Langkah 2: Terapkan MarsApiService

Retrofit membuat API jaringan untuk aplikasi berdasarkan konten dari layanan web. Library ini mengambil data dari layanan web dan merutekannya melalui library pengonversi terpisah yang mengetahui cara mendekode data dan menampilkannya dalam bentuk objek yang berguna. Retrofit mencakup dukungan bawaan untuk format data web populer seperti XML dan JSON. Retrofit pada akhirnya membuat sebagian besar lapisan jaringan untuk Anda, termasuk detail penting seperti menjalankan permintaan pada thread latar belakang.

Class MarsApiService menyimpan lapisan jaringan untuk aplikasi; yaitu, ini adalah API yang akan digunakan ViewModel untuk berkomunikasi dengan layanan web. Di sinilah Anda akan menerapkan API layanan Retrofit.

  1. Buka app/java/network/MarsApiService.kt. Saat ini, file hanya berisi satu hal: konstanta untuk URL dasar layanan web.
private const val BASE_URL = 
   "https://android-kotlin-fun-mars-server.appspot.com"
  1. Tepat di bawah konstanta tersebut, gunakan builder Retrofit untuk membuat objek Retrofit. Impor retrofit2.Retrofit dan retrofit2.converter.scalars.ScalarsConverterFactory bila diminta.
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())
   .baseUrl(BASE_URL)
   .build()

Retrofit memerlukan setidaknya dua hal yang tersedia untuk membangun API layanan web: URI dasar untuk layanan web, dan factory pengonversi. Pengonversi memberi tahu Retrofit apa yang harus dilakukan dengan data yang didapat kembali dari layanan web. Dalam hal ini, Anda ingin Retrofit mengambil respons JSON dari layanan web, dan menampilkannya sebagai String. Retrofit memiliki ScalarsConverter yang mendukung string dan jenis sederhana lainnya, jadi Anda memanggil addConverterFactory() pada builder dengan instance ScalarsConverterFactory. Terakhir, Anda memanggil build() untuk membuat objek Retrofit.

  1. Tepat di bawah panggilan ke builder Retrofit, tentukan antarmuka yang menentukan cara Retrofit berkomunikasi dengan server web menggunakan permintaan HTTP. Impor retrofit2.http.GET dan retrofit2.Call bila diminta.
interface MarsApiService {
    @GET("realestate")
    fun getProperties():
            Call<String>
}

Saat ini tujuannya adalah mendapatkan string respons JSON dari layanan web, dan Anda hanya memerlukan satu metode untuk melakukannya: getProperties(). Untuk memberi tahu Retrofit apa yang harus dilakukan metode ini, gunakan anotasi @GET dan tentukan jalur, atau endpoint, untuk metode layanan web tersebut. Dalam hal ini, endpoint disebut realestate. Saat metode getProperties() dipanggil, Retrofit menambahkan endpoint realestate ke URL dasar (yang Anda tentukan dalam builder Retrofit), dan membuat objek Call. Objek Call tersebut digunakan untuk memulai permintaan.

  1. Di bawah antarmuka MarsApiService, tentukan objek publik yang disebut MarsApi untuk menginisialisasi layanan Retrofit.
object MarsApi {
    val retrofitService : MarsApiService by lazy { 
       retrofit.create(MarsApiService::class.java) }
}

Metode Retrofit create() membuat layanan Retrofit itu sendiri dengan antarmuka MarsApiService. Karena panggilan ini mahal, dan aplikasi hanya memerlukan satu instance layanan Retrofit, Anda mengekspos layanan ke seluruh aplikasi menggunakan objek publik yang disebut MarsApi, dan menginisialisasi layanan Retrofit di sana secara lambat. Setelah semua penyiapan selesai, setiap kali aplikasi Anda memanggil MarsApi.retrofitService, aplikasi akan mendapatkan objek Retrofit singleton yang menerapkan MarsApiService.

Langkah 3: Panggil layanan web di OverviewViewModel

  1. Buka app/java/overview/OverviewViewModel.kt. Scroll ke bawah ke metode getMarsRealEstateProperties().
private fun getMarsRealEstateProperties() {
   _response.value = "Set the Mars API Response here!"
}

Di sinilah Anda akan memanggil layanan Retrofit dan menangani string JSON yang ditampilkan. Saat ini hanya ada string placeholder untuk respons.

  1. Hapus baris placeholder yang menetapkan respons ke "Set the Mars API Response here!"
  2. Di dalam getMarsRealEstateProperties(), tambahkan kode yang ditunjukkan di bawah. Impor retrofit2.Callback dan com.example.android.marsrealestate.network.MarsApi bila diminta.

    Metode MarsApi.retrofitService.getProperties() menampilkan objek Call. Kemudian, Anda dapat memanggil enqueue() pada objek tersebut untuk memulai permintaan jaringan pada thread latar belakang.
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<String> {
})
  1. Klik kata object, yang digarisbawahi dengan warna merah. Pilih Code > Implement methods. Pilih onResponse() dan onFailure() dari daftar.


    Android Studio menambahkan kode dengan TODO di setiap metode:
override fun onFailure(call: Call<String>, t: Throwable) {
       TODO("not implemented") 
}

override fun onResponse(call: Call<String>, 
   response: Response<String>) {
       TODO("not implemented") 
}
  1. Di onFailure(), hapus TODO dan tetapkan _response ke pesan kegagalan, seperti yang ditunjukkan di bawah. _response adalah string LiveData yang menentukan apa yang ditampilkan di tampilan teks. Setiap status perlu memperbarui _response LiveData.

    Callback onFailure() dipanggil saat respons layanan web gagal. Untuk respons ini, setel status _response ke "Failure: " yang digabungkan dengan pesan dari argumen Throwable.
override fun onFailure(call: Call<String>, t: Throwable) {
   _response.value = "Failure: " + t.message
}
  1. Di onResponse(), hapus TODO dan tetapkan _response ke isi respons. Callback onResponse() dipanggil saat permintaan berhasil dan layanan web menampilkan respons.
override fun onResponse(call: Call<String>, 
   response: Response<String>) {
      _response.value = response.body()
}

Langkah 4: Tentukan izin internet

  1. Kompilasi dan jalankan aplikasi MarsRealEstate. Perhatikan bahwa aplikasi segera ditutup dengan error.
  2. Klik tab Logcat di Android Studio dan catat error dalam log, yang diawali dengan baris seperti ini:
Process: com.example.android.marsrealestate, PID: 10646
java.lang.SecurityException: Permission denied (missing INTERNET permission?)

Pesan error memberi tahu Anda bahwa aplikasi Anda mungkin tidak memiliki izin INTERNET. Menghubungkan ke internet menimbulkan masalah keamanan, itulah sebabnya aplikasi tidak memiliki koneksi internet secara default. Anda harus secara eksplisit memberi tahu Android bahwa aplikasi memerlukan akses ke internet.

  1. Buka app/manifests/AndroidManifest.xml. Tambahkan baris ini sebelum tag <application>:
<uses-permission android:name="android.permission.INTERNET" />
  1. Kompilasi dan jalankan aplikasi lagi. Jika semuanya berfungsi dengan benar dengan koneksi internet Anda, Anda akan melihat teks JSON yang berisi data Properti Mars.
  2. Ketuk tombol Kembali di perangkat atau emulator untuk menutup aplikasi.
  3. Aktifkan mode pesawat di perangkat atau emulator Anda, lalu buka kembali aplikasi dari menu Terbaru, atau mulai ulang aplikasi dari Android Studio.


  1. Nonaktifkan mode pesawat lagi.

Sekarang Anda mendapatkan respons JSON dari layanan web Mars, yang merupakan awal yang baik. Namun, yang benar-benar Anda butuhkan adalah objek Kotlin, bukan string JSON besar. Ada library bernama Moshi, yang merupakan parser JSON Android yang mengonversi string JSON menjadi objek Kotlin. Retrofit memiliki pengonversi yang berfungsi dengan Moshi, jadi ini adalah library yang bagus untuk tujuan Anda.

Dalam tugas ini, Anda menggunakan library Moshi dengan Retrofit untuk mengurai respons JSON dari layanan web menjadi objek Kotlin Mars Property yang berguna. Anda mengubah aplikasi sehingga tidak menampilkan JSON mentah, aplikasi akan menampilkan jumlah Properti Mars yang dikembalikan.

Langkah 1: Tambahkan dependensi library Moshi

  1. Buka build.gradle (Module: app).
  2. Di bagian dependensi, tambahkan kode yang ditampilkan di bawah untuk menyertakan dependensi Moshi. Seperti Retrofit, $version_moshi ditentukan secara terpisah dalam file Gradle level project. Dependensi ini menambahkan dukungan untuk library JSON Moshi inti, dan untuk dukungan Kotlin Moshi.
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
  1. Temukan baris untuk pengonversi skalar Retrofit di blok dependencies:
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"
  1. Ubah baris tersebut untuk menggunakan converter-moshi:
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
  1. Klik Sync Now untuk mem-build ulang project dengan dependensi baru.

Langkah 2: Menerapkan class data MarsProperty

Contoh entri respons JSON yang Anda dapatkan dari layanan web terlihat seperti ini:

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

Respons JSON yang ditampilkan di atas adalah array, yang ditunjukkan dengan tanda kurung siku. Array berisi objek JSON, yang dikelilingi oleh tanda kurung kurawal. Setiap objek berisi serangkaian pasangan nama-nilai, yang dipisahkan oleh titik dua. Nama dikelilingi oleh tanda kutip. Nilai dapat berupa angka atau string, dan string juga diapit oleh tanda kutip. Misalnya, price untuk properti ini adalah $450.000 dan img_src adalah URL, yang merupakan lokasi file gambar di server.

Pada contoh di atas, perhatikan bahwa setiap entri properti Mars memiliki pasangan kunci dan nilai JSON berikut:

  • price: harga properti Mars, sebagai angka.
  • id: ID properti, sebagai string.
  • type: "rent" atau "buy".
  • img_src: URL gambar sebagai string.

Moshi mengurai data JSON ini dan mengubahnya menjadi objek Kotlin. Untuk melakukannya, library ini perlu memiliki class data Kotlin untuk menyimpan hasil yang diuraikan, sehingga langkah berikutnya adalah membuat class tersebut.

  1. Buka app/java/network/MarsProperty.kt.
  2. Ganti definisi class MarsProperty yang ada dengan kode berikut:
data class MarsProperty(
   val id: String, val img_src: String,
   val type: String,
   val price: Double
)

Perhatikan bahwa setiap variabel di class MarsProperty sesuai dengan nama kunci dalam objek JSON. Untuk mencocokkan jenis dalam JSON, Anda menggunakan objek String untuk semua nilai kecuali price, yang merupakan Double. Double dapat digunakan untuk merepresentasikan angka JSON apa pun.

Saat Moshi mengurai JSON, Moshi akan mencocokkan kunci berdasarkan nama dan mengisi objek data dengan nilai yang sesuai.

  1. Ganti baris untuk kunci img_src dengan baris yang ditampilkan di bawah. Impor com.squareup.moshi.Json bila diminta.
@Json(name = "img_src") val imgSrcUrl: String,

Terkadang nama kunci dalam respons JSON dapat membuat properti Kotlin membingungkan, atau mungkin tidak cocok dengan gaya coding Anda—misalnya, dalam file JSON, kunci img_src menggunakan garis bawah, sedangkan properti Kotlin biasanya menggunakan huruf besar dan huruf kecil ("camel case").

Untuk menggunakan nama variabel di class data yang berbeda dari nama kunci dalam respons JSON, gunakan anotasi @Json. Dalam contoh ini, nama variabel di class data adalah imgSrcUrl. Variabel dipetakan ke atribut JSON img_src menggunakan @Json(name = "img_src").

Langkah 3: Update MarsApiService dan OverviewViewModel

Dengan adanya class data MarsProperty, Anda kini dapat memperbarui API jaringan dan ViewModel untuk menyertakan data Moshi.

  1. Buka network/MarsApiService.kt. Anda mungkin melihat error class tidak ada untuk ScalarsConverterFactory. Hal ini terjadi karena perubahan dependensi Retrofit yang Anda buat di Langkah 1. Anda akan segera memperbaiki error tersebut.
  2. Di bagian atas file, tepat sebelum builder Retrofit, tambahkan kode berikut untuk membuat instance Moshi. Impor com.squareup.moshi.Moshi dan com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory bila diminta.
private val moshi = Moshi.Builder()
   .add(KotlinJsonAdapterFactory())
   .build()

Mirip dengan yang Anda lakukan dengan Retrofit, di sini Anda membuat objek moshi menggunakan builder Moshi. Agar anotasi Moshi dapat berfungsi dengan baik dengan Kotlin, tambahkan KotlinJsonAdapterFactory, lalu panggil build().

  1. Ubah builder Retrofit untuk menggunakan MoshiConverterFactory, bukan ScalarConverterFactory, dan teruskan instance moshi yang baru saja Anda buat. Impor retrofit2.converter.moshi.MoshiConverterFactory bila diminta.
private val retrofit = Retrofit.Builder()
   .addConverterFactory(MoshiConverterFactory.create(moshi))
   .baseUrl(BASE_URL)
   .build()
  1. Hapus juga impor untuk ScalarConverterFactory.

Kode yang akan dihapus:

import retrofit2.converter.scalars.ScalarsConverterFactory
  1. Perbarui antarmuka MarsApiService agar Retrofit menampilkan daftar objek MarsProperty, bukan menampilkan Call<String>.
interface MarsApiService {
   @GET("realestate")
   fun getProperties():
      Call<List<MarsProperty>>
}
  1. Buka OverviewViewModel.kt. Scroll ke bawah ke panggilan ke getProperties().enqueue() dalam metode getMarsRealEstateProperties().
  2. Ubah argumen menjadi enqueue() dari Callback<String> menjadi Callback<List<MarsProperty>>. Impor com.example.android.marsrealestate.network.MarsProperty bila diminta.
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<List<MarsProperty>> {
  1. Di onFailure(), ubah argumen dari Call<String> menjadi Call<List<MarsProperty>>:
override fun onFailure(call: Call<List<MarsProperty>>, t: Throwable) {
  1. Lakukan perubahan yang sama pada kedua argumen ke onResponse():
override fun onResponse(call: Call<List<MarsProperty>>, 
   response: Response<List<MarsProperty>>) {
  1. Di isi onResponse(), ganti tugas yang ada ke _response.value dengan tugas yang ditunjukkan di bawah. Karena response.body() sekarang berupa daftar objek MarsProperty, ukuran daftar tersebut adalah jumlah properti yang diuraikan. Pesan respons ini mencetak jumlah properti tersebut:
_response.value = 
   "Success: ${response.body()?.size} Mars properties retrieved"
  1. Pastikan mode pesawat dinonaktifkan. Kompilasi dan jalankan aplikasi. Kali ini pesan akan menampilkan jumlah properti yang ditampilkan dari layanan web:

Sekarang layanan API Retrofit berjalan, tetapi menggunakan callback dengan dua metode callback yang harus Anda terapkan. Satu metode menangani keberhasilan dan metode lainnya menangani kegagalan, dan hasil kegagalan melaporkan pengecualian. Kode Anda akan lebih efisien dan lebih mudah dibaca jika Anda dapat menggunakan coroutine dengan penanganan pengecualian, bukan menggunakan callback. Untungnya, Retrofit memiliki library yang mengintegrasikan coroutine.

Dalam tugas ini, Anda akan mengonversi layanan jaringan dan ViewModel untuk menggunakan coroutine.

Langkah 1: Tambahkan dependensi coroutine

  1. Buka build.gradle (Module: app).
  2. Di bagian dependensi, tambahkan dukungan untuk library coroutine Kotlin inti dan library 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. Klik Sync Now untuk mem-build ulang project dengan dependensi baru.

Langkah 2: Perbarui MarsApiService dan OverviewViewModel

  1. Di MarsApiService.kt, perbarui builder Retrofit untuk menggunakan CoroutineCallAdapterFactory. Pembangun lengkap sekarang terlihat seperti ini:
private val retrofit = Retrofit.Builder()
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .addCallAdapterFactory(CoroutineCallAdapterFactory())
        .baseUrl(BASE_URL)
        .build()

Adaptor panggilan menambahkan kemampuan Retrofit untuk membuat API yang menampilkan sesuatu selain class Call default. Dalam hal ini, CoroutineCallAdapterFactory memungkinkan kita mengganti objek Call yang ditampilkan getProperties() dengan objek Deferred.

  1. Dalam metode getProperties(), ubah Call<List<MarsProperty>> menjadi Deferred<List<MarsProperty>>. Impor kotlinx.coroutines.Deferred bila diminta. Metode getProperties() lengkap akan terlihat seperti ini:
@GET("realestate")
fun getProperties():
   Deferred<List<MarsProperty>>

Antarmuka Deferred menentukan tugas coroutine yang menampilkan nilai hasil (Deferred diwarisi dari Job). Antarmuka Deferred menyertakan metode yang disebut await(), yang menyebabkan kode Anda menunggu tanpa memblokir hingga nilai siap, lalu nilai tersebut ditampilkan.

  1. Buka OverviewViewModel.kt. Tepat sebelum blok init, tambahkan tugas coroutine:
private var viewModelJob = Job()
  1. Buat cakupan coroutine untuk tugas baru tersebut menggunakan dispatcher utama:
private val coroutineScope = CoroutineScope(
   viewModelJob + Dispatchers.Main )

Dispatcher Dispatchers.Main menggunakan UI thread untuk tugasnya. Karena Retrofit melakukan semua pekerjaannya di thread latar belakang, tidak ada alasan untuk menggunakan thread lain untuk cakupan. Hal ini memungkinkan Anda memperbarui nilai MutableLiveData dengan mudah saat mendapatkan hasil.

  1. Hapus semua kode di dalam getMarsRealEstateProperties(). Anda akan menggunakan coroutine di sini, bukan panggilan ke enqueue() dan callback onFailure() serta onResponse().
  2. Di dalam getMarsRealEstateProperties(), luncurkan coroutine:
coroutineScope.launch { 

}


Untuk menggunakan objek Deferred yang ditampilkan Retrofit untuk tugas jaringan, Anda harus berada di dalam coroutine, jadi di sini Anda meluncurkan coroutine yang baru saja dibuat. Anda masih menjalankan kode di thread utama, tetapi sekarang Anda membiarkan coroutine mengelola konkurensi.

  1. Di dalam blok peluncuran, panggil getProperties() pada objek retrofitService:
var getPropertiesDeferred = MarsApi.retrofitService.getProperties()

Memanggil getProperties() dari layanan MarsApi akan membuat dan memulai panggilan jaringan di thread latar belakang, serta menampilkan objek Deferred untuk tugas tersebut.

  1. Selain itu, di dalam blok peluncuran, tambahkan blok try/catch untuk menangani pengecualian:
try {

} catch (e: Exception) {
  
}
  1. Di dalam blok try {}, panggil await() pada objek Deferred:
var listResult = getPropertiesDeferred.await()

Memanggil await() pada objek Deferred akan menampilkan hasil dari panggilan jaringan saat nilai sudah siap. Metode await() tidak memblokir, sehingga layanan Mars API mengambil data dari jaringan tanpa memblokir thread saat ini—yang penting karena kita berada dalam cakupan thread UI. Setelah tugas selesai, kode Anda akan melanjutkan eksekusi dari tempat terakhir. Hal ini berada di dalam try {} sehingga Anda dapat menangkap pengecualian.

  1. Juga di dalam blok try {}, setelah metode await(), perbarui pesan respons untuk respons yang berhasil:
_response.value = 
   "Success: ${listResult.size} Mars properties retrieved"
  1. Di dalam blok catch {}, tangani respons kegagalan:
_response.value = "Failure: ${e.message}"


Metode getMarsRealEstateProperties() lengkap kini terlihat seperti ini:

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. Di bagian bawah class, tambahkan callback onCleared() dengan kode ini:
override fun onCleared() {
   super.onCleared()
   viewModelJob.cancel()
}

Pemuatan data harus berhenti saat ViewModel dihancurkan, karena OverviewFragment yang menggunakan ViewModel ini akan hilang. Untuk menghentikan pemuatan saat ViewModel dihancurkan, Anda mengganti onCleared() untuk membatalkan tugas.

  1. Kompilasi dan jalankan aplikasi. Anda akan mendapatkan hasil yang sama kali ini seperti pada tugas sebelumnya (laporan jumlah properti), tetapi dengan kode dan penanganan error yang lebih mudah.

Project Android Studio: MarsRealEstateNetwork

Layanan web REST

  • Layanan web adalah layanan di internet yang memungkinkan aplikasi Anda membuat permintaan dan mendapatkan data kembali.
  • Layanan web umum menggunakan arsitektur REST. Layanan web yang menawarkan arsitektur REST disebut sebagai layanan RESTful. Layanan web RESTful dibuat menggunakan komponen dan protokol web standar.
  • Anda membuat permintaan ke layanan web REST dengan cara standar, melalui URI.
  • Untuk menggunakan layanan web, aplikasi harus membuat koneksi jaringan dan berkomunikasi dengan layanan. Kemudian, aplikasi harus menerima dan mengurai data respons ke dalam format yang dapat digunakan aplikasi.
  • Library Retrofit adalah library klien yang memungkinkan aplikasi Anda membuat permintaan ke layanan web REST.
  • Gunakan pengonversi untuk memberi tahu Retrofit apa yang harus dilakukan dengan data yang dikirimnya ke layanan web dan didapatkan kembali dari layanan web. Misalnya, pengonversi ScalarsConverter memperlakukan data layanan web sebagai String atau primitif lainnya.
  • Agar aplikasi Anda dapat terhubung ke internet, tambahkan izin "android.permission.INTERNET" dalam manifes Android.

Penguraian JSON

  • Respons dari layanan web sering kali diformat dalam JSON, format pertukaran umum untuk merepresentasikan data terstruktur.
  • Objek JSON adalah kumpulan pasangan nilai-kunci. Kumpulan ini terkadang disebut kamus, peta hash, atau array asosiatif.
  • Koleksi objek JSON adalah array JSON. Anda mendapatkan array JSON sebagai respons dari layanan web.
  • Kunci dalam pasangan nilai kunci dikelilingi oleh tanda kutip. Nilai dapat berupa angka atau string. String juga diapit oleh tanda kutip.
  • Library Moshi adalah parser JSON Android yang mengonversi string JSON menjadi objek Kotlin. Retrofit memiliki pengonversi yang berfungsi dengan Moshi.
  • Moshi mencocokkan kunci dalam respons JSON dengan properti dalam objek data yang memiliki nama yang sama.
  • Agar dapat menggunakan nama properti yang berbeda untuk sebuah kunci, anotasikan properti tersebut dengan anotasi @Json dan nama kunci JSON.

Retrofit dan coroutine

  • Adaptor panggilan memungkinkan Retrofit membuat API yang menampilkan sesuatu selain class Call default. Gunakan class CoroutineCallAdapterFactory untuk mengganti Call dengan coroutine Deferred.
  • Gunakan metode await() pada objek Deferred untuk membuat kode coroutine Anda menunggu tanpa memblokir hingga nilai siap, lalu nilai dikembalikan.

Kursus Udacity:

Dokumentasi developer Android:

Dokumentasi Kotlin:

Lainnya:

Bagian ini mencantumkan kemungkinan tugas pekerjaan rumah untuk siswa yang mengerjakan codelab ini sebagai bagian dari kursus yang dipimpin oleh instruktur. Instruktur menentukan hal berikut:

  • Memberikan pekerjaan rumah jika diperlukan.
  • Memberi tahu siswa cara mengirimkan tugas pekerjaan rumah.
  • Memberi nilai tugas pekerjaan rumah.

Instruktur bisa menggunakan saran ini sesuai kebutuhan, dan bebas menugaskan pekerjaan rumah lain yang dirasa cocok.

Jika Anda menyelesaikan codelab ini sendiri, gunakan tugas pekerjaan rumah ini untuk menguji pengetahuan Anda.

Jawab pertanyaan-pertanyaan berikut

Pertanyaan 1

Apa saja dua hal utama yang diperlukan Retrofit untuk membangun API layanan web?

▢ URI dasar untuk layanan web, dan kueri GET.

▢ URI dasar untuk layanan web, dan factory pelaku konversi.

▢ Koneksi jaringan ke layanan web, dan token otorisasi.

▢ Factory pelaku konversi, dan parser untuk respons.

Pertanyaan 2

Apa tujuan dari library Moshi?

▢ Untuk mendapatkan kembali data dari layanan web.

▢ Untuk berinteraksi dengan Retrofit agar dapat membuat permintaan layanan web.

▢ Untuk mengurai respons JSON dari layanan web ke objek data Kotlin.

▢ Untuk mengganti nama objek Kotlin agar sesuai dengan kunci di respons JSON.

Pertanyaan 3

Apa kegunaan adaptor panggilan Retrofit?

▢ Adaptor ini memungkinkan Retrofit menggunakan coroutine.

▢ Adaptor ini mengadaptasi respons layanan web ke dalam objek data Kotlin.

▢ Adaptor ini mengubah panggilan Retrofit menjadi panggilan layanan web.

▢ Adaptor ini menambahkan kemampuan untuk menampilkan sesuatu selain class Call default di Retrofit.

Mulai pelajaran berikutnya: 8.2 Memuat dan menampilkan gambar dari internet

Untuk link ke codelab lain dalam kursus ini, lihat halaman landing codelab Dasar-Dasar Android Kotlin.