1. Sebelum memulai
Sebagian besar aplikasi Android di pasar terhubung ke internet untuk melakukan operasi jaringan, seperti mengambil email, pesan, atau informasi lainnya dari server backend. Gmail, YouTube, dan Google Foto adalah contoh aplikasi yang terhubung ke internet untuk menampilkan data pengguna.
Dalam codelab ini, Anda akan menggunakan library open source dan berbasis komunitas untuk membangun lapisan data dan mendapatkan data dari server backend. Hal ini sangat menyederhanakan pengambilan data dan juga membantu aplikasi mengikuti praktik terbaik Android, seperti melakukan operasi di thread latar belakang. Anda juga akan menampilkan pesan error jika internet lambat atau tidak tersedia, sehingga pengguna akan tetap mendapatkan informasi terkait masalah konektivitas jaringan.
Prasyarat
- Pengetahuan dasar tentang cara membuat fungsi Composable.
- Pengetahuan dasar tentang cara menggunakan komponen arsitektur Android
ViewModel
. - Pengetahuan dasar tentang cara menggunakan coroutine untuk tugas yang berjalan lama.
- Pengetahuan dasar tentang cara menambahkan dependensi di
build.gradle.kts
.
Yang akan Anda pelajari
- Apa itu layanan web REST.
- Cara menggunakan library Retrofit untuk terhubung ke layanan web REST di internet dan mendapatkan respons.
- Cara menggunakan library Serialization (kotlinx.serialization) untuk mengurai respons JSON menjadi objek data.
Yang akan Anda lakukan
- Memodifikasi aplikasi awal untuk membuat permintaan API layanan web dan menangani respons.
- Menerapkan lapisan data untuk aplikasi Anda menggunakan library Retrofit.
- Menguraikan respons JSON dari layanan web ke daftar objek data aplikasi Anda dengan library kotlinx.serialization, dan melampirkannya ke status UI.
- Menggunakan dukungan Retrofit untuk coroutine guna menyederhanakan kode.
Yang Anda perlukan
- Komputer dengan Android Studio
- Kode awal untuk aplikasi Mars Photos
2. Ringkasan aplikasi
Anda bekerja dengan aplikasi bernama Mars Photos, yang menampilkan gambar permukaan Mars. Aplikasi ini terhubung ke layanan web untuk mengambil dan menampilkan foto Mars. Gambar-gambar tersebut adalah foto kehidupan nyata dari Mars yang diambil dari penjelajah Mars NASA. Gambar berikut adalah screenshot aplikasi final, yang berisi petak gambar.
Versi aplikasi yang Anda buat di codelab ini tidak akan memiliki banyak flash visual. Codelab ini berfokus pada bagian lapisan data aplikasi untuk terhubung ke internet dan mendownload data properti mentah menggunakan layanan web. Untuk memastikan aplikasi mengambil dan menguraikan data ini dengan benar, Anda dapat mencetak jumlah foto yang diterima dari server backend dalam composable Text
.
3. Jelajahi aplikasi awal Mars Photos
Mendownload kode awal
Untuk memulai, download kode awal:
Atau, Anda dapat membuat clone repositori GitHub untuk kode tersebut:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-mars-photos.git $ cd basic-android-kotlin-compose-training-mars-photos $ git checkout starter
Anda dapat menjelajahi kode di repositori GitHub Mars Photos
.
Menjalankan kode awal
- Buka project yang telah didownload di Android Studio. Nama folder project adalah
basic-android-kotlin-compose-training-mars-photos
. - Di panel Android, luaskan app > kotlin + java. Perhatikan bahwa aplikasi memiliki folder paket bernama
ui
. Ini adalah lapisan UI aplikasi.
- Jalankan aplikasi. Saat mengompilasi dan menjalankan aplikasi, Anda akan melihat layar berikut dengan teks placeholder di tengah. Di akhir codelab ini, Anda akan memperbarui teks placeholder ini dengan jumlah foto yang diambil.
Panduan kode awal
Dalam tugas ini, Anda akan memahami struktur project. Daftar berikut memberikan panduan terkait file dan folder penting dalam project.
ui\MarsPhotosApp.kt
:
- File ini berisi composable,
MarsPhotosApp
, yang menampilkan konten di layar, seperti panel aplikasi atas dan composableHomeScreen
. Teks placeholder di langkah sebelumnya ditampilkan di composable ini. - Dalam codelab berikutnya, composable ini menampilkan data yang diterima dari server backend foto Mars.
screens\MarsViewModel.kt
:
- File ini adalah model tampilan yang sesuai untuk
MarsPhotosApp
. - Class ini berisi properti
MutableState
bernamamarsUiState
. Memperbarui nilai properti ini akan memperbarui teks placeholder yang ditampilkan di layar. - Metode
getMarsPhotos()
memperbarui respons placeholder. Selanjutnya di codelab, Anda menggunakan metode ini untuk menampilkan data yang diambil dari server. Tujuan codelab ini adalah memperbaruiMutableState
dalamViewModel
menggunakan data nyata yang Anda dapatkan dari internet.
screens\HomeScreen.kt
:
- File ini berisi composable
HomeScreen
danResultScreen
.ResultScreen
memiliki tata letakBox
sederhana yang menampilkan nilaimarsUiState
dalam composableText
.
MainActivity.kt
:
- Satu-satunya tugas untuk aktivitas ini adalah memuat
ViewModel
dan menampilkan composableMarsPhotosApp
.
4. Pengantar layanan web
Dalam codelab ini, Anda membuat lapisan untuk layanan jaringan yang berkomunikasi dengan server backend dan mengambil data yang diperlukan. Anda menggunakan library pihak ketiga, yang disebut Retrofit, untuk menerapkan tugas ini. Anda akan mempelajari hal ini lebih lanjut nanti. ViewModel
berkomunikasi dengan lapisan data, dan aplikasi lainnya bersifat transparan untuk penerapan ini.
MarsViewModel
bertanggung jawab membuat panggilan jaringan untuk mendapatkan data foto Mars. Di ViewModel
, Anda menggunakan MutableState
untuk mengupdate UI aplikasi saat data berubah.
5. Layanan web dan Retrofit
Data foto Mars disimpan di server web. Untuk memasukkan data ini ke dalam aplikasi, Anda harus membuat koneksi dan berkomunikasi dengan server di internet.
Sebagian besar server web saat ini menjalankan layanan web menggunakan arsitektur web stateless umum yang dikenal sebagai REST, yang merupakan kependekan dari REpresentational State Transfer. Layanan web yang menawarkan arsitektur ini dikenal sebagai layanan RESTful.
Permintaan dibuat untuk layanan web RESTful dengan cara standar, melalui Uniform Resource Identifiers (URI). URI mengidentifikasi resource di server berdasarkan nama, tanpa menyiratkan lokasi atau cara mengaksesnya. Misalnya, dalam aplikasi untuk pelajaran ini, Anda akan mengambil URL gambar menggunakan URI server berikut. (Server ini menghosting real estate Mars dan foto Mars):
android-kotlin-fun-mars-server.appspot.com
URL (Uniform Resource Locator) adalah subset URI yang menentukan lokasi resource dan mekanisme untuk mengambilnya.
Contoh:
URL berikut berisi daftar properti real estate yang tersedia di Mars:
https://android-kotlin-fun-mars-server.appspot.com/realestate
URL berikut mendapatkan daftar foto Mars:
https://android-kotlin-fun-mars-server.appspot.com/photos
URL ini merujuk ke resource yang diidentifikasi, seperti /realestate atau /photos, yang dapat diperoleh melalui Hypertext Transfer Protocol (http:) dari jaringan. Anda menggunakan endpoint /photos dalam codelab ini. Endpoint adalah URL yang memungkinkan Anda mengakses layanan web yang berjalan di server.
Permintaan layanan web
Setiap permintaan layanan web berisi URI dan ditransfer ke server menggunakan protokol HTTP yang sama dengan yang digunakan oleh browser web, seperti Chrome. Permintaan HTTP berisi operasi untuk memberi tahu server apa yang harus dilakukan.
Operasi HTTP umum meliputi:
- GET untuk mengambil data server.
- POST untuk membuat data baru di server.
- PUT untuk memperbarui data yang ada di server.
- DELETE untuk menghapus data dari server.
Aplikasi Anda membuat permintaan GET HTTP ke server untuk mendapatkan informasi foto Mars, lalu server menampilkan respons ke aplikasi Anda, termasuk URL gambar.
Respons dari layanan web diformat dalam salah satu format data umum, seperti XML (eXtensible Markup Language) atau JSON (JavaScript Object Notation). Format JSON mewakili data terstruktur dalam key-value pair. Aplikasi berkomunikasi dengan REST API menggunakan JSON, yang Anda pelajari lebih lanjut dalam tugas berikutnya.
Dalam tugas ini, Anda membuat koneksi jaringan ke server, berkomunikasi dengan server, dan menerima respons JSON. Anda akan menggunakan server backend yang sudah ditulis untuk Anda. Dalam codelab ini, Anda menggunakan library Retrofit, library pihak ketiga untuk berkomunikasi dengan server backend.
Library Eksternal
Library eksternal atau library pihak ketiga sama seperti ekstensi untuk API Android inti. Library yang Anda gunakan dalam kursus ini adalah open source, dikembangkan oleh komunitas, dan dikelola oleh kontribusi kolektif dari komunitas Android yang besar di seluruh dunia. Library ini membantu developer Android seperti Anda untuk membangun aplikasi yang lebih baik.
Library Retrofit
Library Retrofit yang Anda gunakan dalam codelab ini untuk berkomunikasi dengan layanan web RESTful Mars adalah contoh bagus dari library yang didukung dan dikelola dengan baik. Anda dapat mengetahuinya dengan melihat halaman GitHub dan meninjau masalah terbuka dan tertutup (beberapa di antaranya adalah permintaan fitur). Jika developer rutin mengatasi masalah dan merespons permintaan fitur, berarti library mungkin dikelola dengan baik dan merupakan kandidat yang baik untuk digunakan dalam aplikasi. Anda juga dapat melihat dokumentasi Retrofit untuk mempelajari library lebih lanjut.
Library Retrofit berkomunikasi dengan backend REST. Kode ini akan menghasilkan kode, tetapi Anda harus menyediakan URI untuk layanan web berdasarkan parameter yang kita teruskan. Anda akan mempelajari lebih lanjut topik ini di bagian berikutnya.
Menambahkan dependensi Retrofit
Android Gradle memungkinkan Anda menambahkan library eksternal ke project Anda. Selain dependensi library, Anda juga perlu menyertakan repositori tempat library dihosting.
- Buka file gradle level modul
build.gradle.kts (Module :app)
. - Di bagian
dependencies
, tambahkan baris berikut untuk library Retrofit:
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
// Retrofit with Scalar Converter
implementation("com.squareup.retrofit2:converter-scalars:2.9.0")
Kedua library bekerja bersama. Dependensi pertama adalah untuk library Retrofit2, dan dependensi kedua adalah untuk pengonversi skalar Retrofit. Retrofit2 adalah versi library Retrofit yang diperbarui. Pengonversi skalar ini memungkinkan Retrofit menampilkan hasil JSON sebagai String
. JSON adalah format untuk menyimpan dan memindahkan data antara klien dan server. Anda akan mempelajari JSON di bagian selanjutnya.
- Klik Sync Now untuk membangun ulang project dengan dependensi baru.
6. Menghubungkan ke Internet
Anda menggunakan library Retrofit untuk berkomunikasi dengan layanan web Mars dan menampilkan respons JSON mentah sebagai String
. Placeholder Text
menampilkan string respons JSON yang ditampilkan atau pesan yang menunjukkan error koneksi.
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, seperti String
. Retrofit mencakup dukungan bawaan untuk format data populer, seperti XML dan JSON. Retrofit pada akhirnya membuat kode untuk memanggil dan memakai layanan ini untuk Anda, termasuk detail penting, seperti menjalankan permintaan pada thread latar belakang.
Dalam tugas ini, Anda menambahkan lapisan data ke project Mars Photos yang digunakan ViewModel
untuk berkomunikasi dengan layanan web. Anda menerapkan API layanan Retrofit, dengan langkah-langkah berikut:
- Buat sumber data, class
MarsApiService
. - Buat objek Retrofit dengan URL dasar dan factory pengonversi untuk mengonversi string.
- Buat antarmuka yang menjelaskan cara Retrofit berkomunikasi dengan server web.
- Buat layanan Retrofit dan ekspos instance-nya ke layanan API ke seluruh aplikasi.
Terapkan langkah-langkah di atas:
- Klik kanan pada paket com.example.marsphotos di panel project Android Anda dan pilih New > Package.
- Di pop-up, tambahkan network ke akhir nama paket yang disarankan.
- Buat file Kotlin baru di bawah paket baru. Beri nama
MarsApiService
. - Buka
network/MarsApiService.kt
. - Tambahkan konstanta berikut ke URL dasar untuk layanan web.
private const val BASE_URL =
"https://android-kotlin-fun-mars-server.appspot.com"
- Tambahkan builder Retrofit tepat di bawah konstanta tersebut untuk membangun dan membuat objek Retrofit.
import retrofit2.Retrofit
private val retrofit = Retrofit.Builder()
Retrofit memerlukan URI dasar untuk layanan web dan factory pengonversi untuk membangun API layanan web. 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.
- Panggil
addConverterFactory()
pada builder dengan instanceScalarsConverterFactory
.
import retrofit2.converter.scalars.ScalarsConverterFactory
private val retrofit = Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
- Tambahkan URL dasar untuk layanan web menggunakan metode
baseUrl()
. - Panggil
build()
untuk membuat objek Retrofit.
private val retrofit = Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(BASE_URL)
.build()
- Pada panggilan ke builder Retrofit, tentukan antarmuka yang disebut
MarsApiService
yang menentukan cara Retrofit berkomunikasi dengan server web menggunakan permintaan HTTP.
interface MarsApiService {
}
- Tambahkan fungsi yang bernama
getPhotos()
ke antarmukaMarsApiService
untuk mendapatkan string respons dari layanan web.
interface MarsApiService {
fun getPhotos()
}
- Gunakan anotasi
@GET
untuk memberi tahu Retrofit bahwa ini adalah permintaan GET dan tentukan endpoint untuk metode layanan web tersebut. Dalam kasus ini, endpoint-nya adalahphotos
. Seperti yang disebutkan dalam tugas sebelumnya, Anda akan menggunakan endpoint /photos dalam codelab ini.
import retrofit2.http.GET
interface MarsApiService {
@GET("photos")
fun getPhotos()
}
Saat metode getPhotos()
dipanggil, Retrofit menambahkan endpoint photos
ke URL dasar—yang Anda tentukan dalam builder Retrofit—yang digunakan untuk memulai permintaan.
- Tambahkan jenis nilai yang ditampilkan fungsi ke
String
.
interface MarsApiService {
@GET("photos")
fun getPhotos(): String
}
Deklarasi objek
Pada Kotlin, deklarasi objek digunakan untuk mendeklarasikan objek singleton. Pola Singleton memastikan bahwa satu, dan hanya satu, instance objek yang dibuat dan memiliki satu titik akses global ke objek tersebut. Inisialisasi objek adalah thread-safe dan dilakukan pada akses pertama.
Berikut adalah contoh deklarasi objek dan aksesnya. Deklarasi objek selalu memiliki nama yang mengikuti kata kunci object
.
Contoh:
// Example for Object declaration, do not copy over
object SampleDataProvider {
fun register(provider: SampleProvider) {
// ...
}
// ...
}
// To refer to the object, use its name directly.
SampleDataProvider.register(...)
Panggilan ke fungsi create()
pada objek Retrofit sangat mahal dalam hal memori, kecepatan, dan performa. Aplikasi ini hanya memerlukan satu instance layanan Retrofit API, sehingga Anda mengekspos layanan ke seluruh aplikasi menggunakan deklarasi objek.
- Di luar deklarasi antarmuka
MarsApiService
, tentukan objek publik yang disebutMarsApi
untuk menginisialisasi layanan Retrofit. Objek ini adalah objek singleton publik yang dapat diakses oleh aplikasi lainnya.
object MarsApi {}
- Di dalam deklarasi objek
MarsApi
, tambahkan properti objek retrofit yang diinisialisasi dengan lambat bernamaretrofitService
dari jenisMarsApiService
. Anda melakukan inisialisasi lambat ini, untuk memastikan diinisialisasi saat penggunaan pertama. Abaikan error, yang Anda perbaiki pada langkah berikutnya.
object MarsApi {
val retrofitService : MarsApiService by lazy {}
}
- Inisialisasi variabel
retrofitService
menggunakan metoderetrofit.create()
dengan antarmukaMarsApiService
.
object MarsApi {
val retrofitService : MarsApiService by lazy {
retrofit.create(MarsApiService::class.java)
}
}
Penyiapan Retrofit selesai! Setiap kali aplikasi Anda memanggil MarsApi.retrofitService
, pemanggil akan mengakses objek Retrofit singleton yang sama yang menerapkan MarsApiService
, yang dibuat pada akses pertama. Pada tugas berikutnya, Anda akan menggunakan objek Retrofit yang Anda implementasikan.
Memanggil layanan web di MarsViewModel
Pada langkah ini, Anda akan menerapkan metode getMarsPhotos()
yang memanggil layanan REST, lalu menangani string JSON yang ditampilkan.
ViewModelScope
viewModelScope
adalah cakupan coroutine bawaan yang ditentukan untuk setiap ViewModel
dalam aplikasi Anda. Setiap coroutine yang diluncurkan dalam cakupan ini akan dibatalkan secara otomatis jika ViewModel
dihapus.
Anda dapat menggunakan viewModelScope
untuk meluncurkan coroutine dan membuat permintaan layanan web di latar belakang. Karena viewModelScope
adalah milik ViewModel
, permintaan akan berlanjut meskipun aplikasi mengalami perubahan konfigurasi.
- Di file
MarsApiService.kt
, jadikangetPhotos()
sebagai fungsi penangguhan untuk menjadikannya asinkron dan tidak memblokir thread panggilan. Anda memanggil fungsi ini dari dalamviewModelScope
.
@GET("photos")
suspend fun getPhotos(): String
- Buka file
ui/screens/MarsViewModel.kt
. Scroll ke bawah, ke metodegetMarsPhotos()
. Hapus baris yang menetapkan respons status ke"Set the Mars API Response here!"
sehingga metodegetMarsPhotos()
kosong.
private fun getMarsPhotos() {}
- Di dalam
getMarsPhotos()
, luncurkan coroutine menggunakanviewModelScope.launch
.
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
private fun getMarsPhotos() {
viewModelScope.launch {}
}
- Di dalam
viewModelScope
, gunakan objek singletonMarsApi
untuk memanggil metodegetPhotos()
dari antarmukaretrofitService
. Simpan respons yang ditampilkan dalamval
yang bernamalistResult
.
import com.example.marsphotos.network.MarsApi
viewModelScope.launch {
val listResult = MarsApi.retrofitService.getPhotos()
}
- Tetapkan hasil yang baru saja diterima dari server backend ke
marsUiState
.marsUiState
adalah objek status yang dapat diubah yang mewakili status permintaan web terbaru.
val listResult = MarsApi.retrofitService.getPhotos()
marsUiState = listResult
- Jalankan aplikasi. Perhatikan bahwa aplikasi segera ditutup, dan mungkin menampilkan atau tidak menampilkan pop-up error. Ini adalah error aplikasi.
- Klik tab Logcat di Android Studio dan catat error dalam log, yang diawali dengan baris seperti ini: "
------- beginning of crash
"
--------- beginning of crash 22803-22865/com.example.android.marsphotos E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher Process: com.example.android.marsphotos, PID: 22803 java.lang.SecurityException: Permission denied (missing INTERNET permission?) ...
Pesan error ini menunjukkan bahwa aplikasi mungkin tidak memiliki izin INTERNET
. Tugas berikutnya menjelaskan cara menambahkan izin internet ke aplikasi dan menyelesaikan masalah ini.
7. Menambahkan izin Internet dan Penanganan Pengecualian
Izin Android
Tujuan izin di Android adalah untuk melindungi privasi pengguna Android. Aplikasi Android harus mendeklarasikan atau meminta izin untuk mengakses data pengguna yang sifatnya sensitif, seperti kontak, log panggilan, dan fitur sistem tertentu, seperti kamera atau internet.
Agar aplikasi Anda dapat mengakses Internet, aplikasi memerlukan izin INTERNET
. Menghubungkan ke internet menimbulkan masalah keamanan, itulah sebabnya aplikasi tidak memiliki koneksi internet secara default. Anda harus secara eksplisit mendeklarasikan bahwa aplikasi memerlukan akses ke internet. Deklarasi ini dianggap sebagai izin normal. Untuk mempelajari izin Android dan jenisnya lebih lanjut, lihat Izin di Android.
Pada langkah ini, aplikasi Anda mendeklarasikan izin yang diperlukan dengan menyertakan tag <uses-permission>
dalam file AndroidManifest.xml
.
- Buka
manifests/AndroidManifest.xml
. Tambahkan baris ini sebelum tag<application>
:
<uses-permission android:name="android.permission.INTERNET" />
- Kompilasi dan jalankan aplikasi lagi.
Jika memiliki koneksi internet yang aktif, Anda akan melihat teks JSON yang berisi data terkait foto Mars. Perhatikan bagaimana id
dan img_src
diulang untuk setiap catatan gambar. Anda akan mempelajari lebih lanjut format JSON nanti di codelab.
- Ketuk tombol Kembali di perangkat atau emulator untuk menutup aplikasi.
Penanganan Pengecualian
Ada bug dalam kode Anda. Lakukan langkah-langkah berikut untuk melihatnya:
- Setel perangkat atau emulator ke Mode Pesawat untuk menyimulasikan error koneksi jaringan.
- Buka kembali aplikasi dari menu Terbaru, atau jalankan aplikasi dari Android Studio.
- Klik tab Logcat di Android Studio dan catat pengecualian fatal dalam log, yang terlihat seperti berikut ini:
3302-3302/com.example.android.marsphotos E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.android.marsphotos, PID: 3302
Pesan error ini menunjukkan aplikasi mencoba untuk terhubung dan waktu habis. Pengecualian seperti ini sangat umum terjadi secara real time. Tidak seperti masalah izin, error ini bukan masalah yang dapat Anda perbaiki, tetapi Anda dapat menanganinya. Pada langkah berikutnya, Anda mempelajari cara menangani pengecualian tersebut.
Pengecualian
Pengecualian adalah error yang dapat terjadi selama runtime, bukan waktu kompilasi, dan menghentikan aplikasi secara tiba-tiba tanpa memberi tahu pengguna. Hal ini dapat mengakibatkan pengalaman pengguna yang buruk. Penanganan pengecualian adalah mekanisme yang digunakan untuk mencegah aplikasi berhenti tiba-tiba dan menangani situasi dengan cara yang mudah digunakan.
Alasan pengecualian bisa sesederhana pembagian dengan nol atau error dengan koneksi jaringan. Pengecualian ini mirip dengan IllegalArgumentException
yang dibahas di codelab sebelumnya.
Contoh potensi masalah saat menghubungkan ke server meliputi:
- URL atau URI yang digunakan di API salah.
- Server tidak tersedia, dan aplikasi tidak dapat terhubung.
- Masalah latensi jaringan.
- Koneksi internet buruk atau tidak ada di perangkat.
Pengecualian ini tidak dapat ditangani selama waktu kompilasi, tetapi Anda dapat menggunakan blok try-catch
untuk menangani pengecualian dalam runtime. Untuk mempelajari lebih lanjut, baca Pengecualian.
Contoh sintaksis untuk blok try-catch
try {
// some code that can cause an exception.
}
catch (e: SomeException) {
// handle the exception to avoid abrupt termination.
}
Di dalam blok try
, Anda akan menambahkan kode tempat mengantisipasi pengecualian. Di aplikasi Anda, ini adalah panggilan jaringan. Di blok catch
, Anda harus menerapkan kode yang mencegah penghentian aplikasi secara tiba-tiba. Jika ada pengecualian, blok catch
akan dijalankan untuk pulih dari error, bukan menghentikan aplikasi secara tiba-tiba.
- Di
getMarsPhotos()
, di dalam bloklaunch
, tambahkan bloktry
di sekitar panggilanMarsApi
untuk menangani pengecualian. - Tambahkan blok
catch
setelah bloktry
.
import java.io.IOException
viewModelScope.launch {
try {
val listResult = MarsApi.retrofitService.getPhotos()
marsUiState = listResult
} catch (e: IOException) {
}
}
- Jalankan aplikasi sekali lagi. Perhatikan bahwa aplikasi tidak mengalami error kali ini.
Menambahkan UI Status
Di class MarsViewModel
, status permintaan web terbaru, marsUiState
, disimpan sebagai objek status yang dapat diubah. Namun, class ini tidak memiliki kemampuan untuk menyimpan status yang berbeda: memuat, berhasil, dan gagal.
- Status Memuat menunjukkan bahwa aplikasi sedang menunggu data.
- Status Berhasil menunjukkan bahwa data berhasil diambil dari layanan web.
- Status Error menunjukkan error jaringan atau koneksi.
Untuk mewakili ketiga status ini dalam aplikasi, Anda menggunakan antarmuka tertutup. sealed interface
memudahkan pengelolaan status dengan membatasi kemungkinan nilai. Di aplikasi Mars Photos, Anda membatasi respons web marsUiState
ke tiga status (objek class data): memuat, berhasil, dan error, yang terlihat seperti kode berikut:
// No need to copy over
sealed interface MarsUiState {
data class Success : MarsUiState
data class Loading : MarsUiState
data class Error : MarsUiState
}
Dalam cuplikan kode di atas, untuk respons yang berhasil, Anda akan menerima informasi foto Mars dari server. Untuk menyimpan data, tambahkan parameter konstruktor ke class data Success
.
Dalam kasus status Loading
dan Error
, Anda tidak perlu menetapkan data baru dan membuat objek baru; Anda hanya meneruskan respons web. Ubah class data
menjadi Object
guna membuat objek untuk respons web.
- Buka file
ui/MarsViewModel.kt
. Setelah pernyataan impor, tambahkan antarmuka tertutupMarsUiState
. Penambahan ini membuat nilai objekMarsUiState
dapat dilengkapi.
sealed interface MarsUiState {
data class Success(val photos: String) : MarsUiState
object Error : MarsUiState
object Loading : MarsUiState
}
- Di dalam class
MarsViewModel
, perbarui definisimarsUiState
. Ubah jenis keMarsUiState
danMarsUiState.Loading
sebagai nilai defaultnya. Buat penyetel menjadi pribadi untuk melindungi penulisan kemarsUiState
.
var marsUiState: MarsUiState by mutableStateOf(MarsUiState.Loading)
private set
- Scroll ke bawah, ke metode
getMarsPhotos()
. Perbarui nilaimarsUiState
keMarsUiState.Success
dan teruskanlistResult
.
val listResult = MarsApi.retrofitService.getPhotos()
marsUiState = MarsUiState.Success(listResult)
- Di dalam blok
catch
, tangani respons kegagalan. SetelMarsUiState
keError
.
catch (e: IOException) {
marsUiState = MarsUiState.Error
}
- Anda dapat mencabut penetapan
marsUiState
dari bloktry-catch
. Fungsi Anda yang sudah selesai akan terlihat seperti kode berikut:
private fun getMarsPhotos() {
viewModelScope.launch {
marsUiState = try {
val listResult = MarsApi.retrofitService.getPhotos()
MarsUiState.Success(listResult)
} catch (e: IOException) {
MarsUiState.Error
}
}
}
- Dalam file
screens/HomeScreen.kt
, tambahkan ekspresiwhen
dimarsUiState
. JikamarsUiState
adalahMarsUiState.Success
, panggilResultScreen
dan teruskanmarsUiState.photos
. Abaikan error untuk saat ini.
import androidx.compose.foundation.layout.fillMaxWidth
fun HomeScreen(
marsUiState: MarsUiState,
modifier: Modifier = Modifier
) {
when (marsUiState) {
is MarsUiState.Success -> ResultScreen(
marsUiState.photos, modifier = modifier.fillMaxWidth()
)
}
}
- Di dalam blok
when
, tambahkan pemeriksaan untukMarsUiState.Loading
danMarsUiState.Error
. Minta aplikasi menampilkan composableLoadingScreen
,ResultScreen
, danErrorScreen
yang Anda terapkan nanti.
import androidx.compose.foundation.layout.fillMaxSize
fun HomeScreen(
marsUiState: MarsUiState,
modifier: Modifier = Modifier
) {
when (marsUiState) {
is MarsUiState.Loading -> LoadingScreen(modifier = modifier.fillMaxSize())
is MarsUiState.Success -> ResultScreen(
marsUiState.photos, modifier = modifier.fillMaxWidth()
)
is MarsUiState.Error -> ErrorScreen( modifier = modifier.fillMaxSize())
}
}
- Buka
res/drawable/loading_animation.xml
. Drawable ini adalah animasi yang memutar drawable gambar,loading_img.xml
, mengitari titik tengah. (Anda tidak melihat animasinya di pratinjau.)
- Pada file
screens/HomeScreen.kt
, di bawah composableHomeScreen
, tambahkan fungsi composableLoadingScreen
berikut untuk menampilkan animasi pemuatan. Resource drawableloading_img
disertakan dalam kode awal.
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.Image
@Composable
fun LoadingScreen(modifier: Modifier = Modifier) {
Image(
modifier = modifier.size(200.dp),
painter = painterResource(R.drawable.loading_img),
contentDescription = stringResource(R.string.loading)
)
}
- Di bawah composable
LoadingScreen
, tambahkan fungsi composableErrorScreen
berikut sehingga aplikasi dapat menampilkan pesan error.
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
@Composable
fun ErrorScreen(modifier: Modifier = Modifier) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.drawable.ic_connection_error), contentDescription = ""
)
Text(text = stringResource(R.string.loading_failed), modifier = Modifier.padding(16.dp))
}
}
- Jalankan aplikasi lagi dengan mengaktifkan Mode Pesawat. Aplikasi tidak menutup sendiri sekarang, dan menampilkan pesan error berikut:
- Nonaktifkan Mode Pesawat di ponsel atau emulator. Jalankan dan uji aplikasi Anda untuk memastikan semuanya berfungsi dengan benar dan Anda dapat melihat string JSON.
8. Mengurai respons JSON dengan kotlinx.serialization
JSON
Data yang diminta biasanya diformat dalam salah satu format data umum seperti XML atau JSON. Setiap panggilan menampilkan data terstruktur, dan aplikasi Anda perlu mengetahui struktur tersebut untuk membaca data dari respons.
Misalnya, di aplikasi ini, Anda mengambil data dari server https:// android-kotlin-fun-mars-server.appspot.com/photos. Saat memasukkan URL ini di browser, Anda akan melihat daftar ID dan URL gambar permukaan Mars dalam format JSON.
Struktur respons JSON sampel
Struktur respons JSON memiliki fitur berikut:
- Respons JSON adalah array, yang ditunjukkan dengan tanda kurung siku. Array berisi objek JSON.
- Objek JSON dikelilingi oleh tanda kurung kurawal.
- Setiap objek JSON berisi kumpulan key-value pair yang dipisahkan oleh koma.
- Titik dua memisahkan kunci dan nilai dalam pasangan.
- Nama dikelilingi oleh tanda kutip.
- Nilai dapat berupa angka, string, boolean, array, objek (objek JSON), atau null.
Misalnya, img_src
adalah URL, yang berupa string. Saat menempelkan URL ke browser web, Anda akan melihat gambar permukaan Mars.
Di aplikasi, sekarang Anda mendapatkan respons JSON dari layanan web Mars, yang merupakan awal yang baik. Namun, yang benar-benar Anda perlukan untuk menampilkan gambar adalah objek Kotlin, bukan string JSON besar. Proses ini disebut deserialisasi.
Serialisasi adalah proses mengonversi data yang digunakan oleh aplikasi ke format yang dapat ditransfer melalui jaringan. Berbeda dengan serialisasi, deserialisasi adalah proses membaca data dari sumber eksternal (seperti server) dan mengonversinya menjadi objek runtime. Keduanya adalah komponen penting dari sebagian besar aplikasi yang bertukar data melalui jaringan.
kotlinx.serialization
menyediakan kumpulan library yang mengonversi string JSON menjadi objek Kotlin. Ada komunitas yang dikembangkan oleh library pihak ketiga yang berfungsi dengan Retrofit, Pengonversi Serialisasi Kotlin.
Dalam tugas ini, Anda menggunakan library kotlinx.serialization
, untuk mengurai respons JSON dari layanan web menjadi objek Kotlin berguna yang menampilkan foto Mars. Anda mengubah aplikasi sehingga tidak menampilkan JSON mentah, aplikasi akan menampilkan jumlah foto Mars yang dikembalikan.
Menambahkan dependensi library kotlinx.serialization
- Buka
build.gradle.kts (Module :app)
. - Di blok
plugins
, tambahkan pluginkotlinx serialization
.
id("org.jetbrains.kotlin.plugin.serialization") version "1.8.10"
- Di bagian
dependencies
, tambahkan kode berikut untuk menyertakan dependensikotlinx.serialization
. Dependensi ini menyediakan serialisasi JSON untuk project Kotlin.
// Kotlin serialization
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
- Temukan baris untuk pengonversi skalar Retrofit di blok
dependencies
lalu ubah untuk menggunakankotlinx-serialization-converter
:
Ganti kode berikut
// Retrofit with scalar Converter
implementation("com.squareup.retrofit2:converter-scalars:2.9.0")
dengan kode berikut
// Retrofit with Kotlin serialization Converter
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
implementation("com.squareup.okhttp3:okhttp:4.11.0")
- Klik Sync Now untuk membangun ulang project dengan dependensi baru.
Menerapkan class data Mars Photo
Contoh entri respons JSON yang Anda dapatkan dari layanan web terlihat seperti berikut, mirip dengan yang Anda lihat sebelumnya:
[
{
"id":"424906",
"img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"
},
...]
Pada contoh di atas, perhatikan bahwa setiap entri foto Mars memiliki pasangan kunci dan nilai JSON berikut:
id
: ID properti, sebagai string. Karena diawali dan diakhiri dengan tanda kutip (" "
), jenis file tersebut adalahString
, bukanInteger
.img_src
: URL gambar, sebagai string.
kotlinx.serialization mengurai data JSON ini dan mengubahnya menjadi objek Kotlin. Untuk melakukannya, kotlinx.serialization perlu memiliki class data Kotlin untuk menyimpan hasil yang diuraikan. Pada langkah ini, Anda akan membuat class data MarsPhoto
.
- Klik kanan pada paket network dan pilih New > Kotlin File/Class.
- Di dialog pilih Class dan masukkan
MarsPhoto
sebagai nama class. Tindakan ini akan membuat file baru bernamaMarsPhoto.kt
dalam paketnetwork
. - Jadikan
MarsPhoto
class data dengan menambahkan kata kuncidata
sebelum definisi class. - Ubah tanda kurung kurawal
{}
menjadi tanda kurung()
. Perubahan ini akan menimbulkan error karena class data harus memiliki setidaknya satu properti yang ditentukan.
data class MarsPhoto()
- Tambahkan properti berikut ke definisi class
MarsPhoto
.
data class MarsPhoto(
val id: String, val img_src: String
)
- Untuk membuat class
MarsPhoto
yang dapat diserialisasi dianotasi dengan@Serializable
.
import kotlinx.serialization.Serializable
@Serializable
data class MarsPhoto(
val id: String, val img_src: String
)
Perhatikan bahwa setiap variabel di class MarsPhoto
sesuai dengan nama kunci dalam objek JSON. Untuk mencocokkan jenis dalam respons JSON tertentu, Anda menggunakan objek String
untuk semua nilai.
Saat kotlinx serialization
mengurai JSON, fungsi tersebut akan mencocokkan kunci menurut nama dan mengisi objek data dengan nilai yang sesuai.
Anotasi @SerialName
Terkadang nama kunci dalam respons JSON dapat membuat properti Kotlin membingungkan atau mungkin tidak cocok dengan gaya coding yang disarankan. Misalnya, dalam file JSON, kunci img_src
menggunakan garis bawah, sedangkan konvensi Kotlin untuk properti 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 @SerialName
. Pada contoh berikut, nama variabel di class data adalah imgSrc
. Variabel dapat dipetakan ke atribut JSON img_src
menggunakan @SerialName(value = "img_src")
.
- Ganti baris untuk kunci
img_src
dengan baris yang ditampilkan di bawah.
import kotlinx.serialization.SerialName
@SerialName(value = "img_src")
val imgSrc: String
Mengupdate MarsApiService dan MarsViewModel
Dalam tugas ini, Anda akan menggunakan pengonversi kotlinx.serialization
untuk mengonversi objek JSON ke objek Kotlin.
- Buka
network/MarsApiService.kt
. - Perhatikan error referensi yang belum terselesaikan untuk
ScalarsConverterFactory
. Error ini adalah hasil dari perubahan dependensi Retrofit di bagian sebelumnya. - Hapus impor untuk
ScalarConverterFactory
. Anda memperbaiki error lainnya nanti.
Hapus:
import retrofit2.converter.scalars.ScalarsConverterFactory
- Pada deklarasi objek
retrofit
, ubah builder Retrofit untuk menggunakankotlinx.serialization
, bukanScalarConverterFactory
.
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType
private val retrofit = Retrofit.Builder()
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.baseUrl(BASE_URL)
.build()
Setelah menempatkan kotlinx.serialization
, Anda dapat meminta Retrofit untuk menampilkan daftar objek MarsPhoto
dari array JSON, bukan menampilkan string JSON.
- Perbarui antarmuka
MarsApiService
untuk Retrofit guna menampilkan daftar objekMarsPhoto
, bukanString
.
interface MarsApiService {
@GET("photos")
suspend fun getPhotos(): List<MarsPhoto>
}
- Buat perubahan yang serupa pada
viewModel
. BukaMarsViewModel.kt
dan scroll ke bawah ke metodegetMarsPhotos()
.
Dalam metode getMarsPhotos()
, listResult
adalah List<MarsPhoto>
, bukan String
lagi. Ukuran daftar tersebut adalah jumlah foto yang diterima dan diuraikan.
- Untuk mencetak jumlah foto yang diambil, update
marsUiState
sebagai berikut:
val listResult = MarsApi.retrofitService.getPhotos()
marsUiState = MarsUiState.Success(
"Success: ${listResult.size} Mars photos retrieved"
)
- Pastikan Mode Pesawat dinonaktifkan pada perangkat atau emulator. Mengompilasi dan menjalankan aplikasi.
Kali ini, pesan akan menampilkan jumlah properti yang ditampilkan dari layanan web, dan bukan string JSON besar:
9. Kode solusi
Untuk mendownload kode codelab yang sudah selesai, Anda dapat menggunakan perintah git berikut:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-mars-photos.git $ cd basic-android-kotlin-compose-training-mars-photos $ git checkout repo-starter
Atau, Anda dapat mendownload repositori sebagai file ZIP, lalu mengekstraknya, dan membukanya di Android Studio.
Jika Anda ingin melihat kode solusi untuk codelab ini, lihat kode tersebut di GitHub.
10. Ringkasan
Layanan web REST
- Layanan web adalah fungsi berbasis software, yang ditawarkan melalui 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,
ScalarsConverter
memperlakukan data layanan web sebagaiString
atau primitif lainnya. - Agar aplikasi Anda dapat terhubung ke internet, tambahkan izin
"android.permission.INTERNET"
dalam manifes Android. - Inisialisasi lambat mendelegasikan pembuatan objek saat pertama kali digunakan. Tindakan ini akan membuat referensi, tetapi tidak membuat objek. Saat objek diakses untuk pertama kalinya, referensi akan dibuat dan digunakan setiap kali setelahnya.
Penguraian JSON
- Respons dari layanan web sering kali diformat dalam JSON, format umum untuk mewakili data terstruktur.
- Objek JSON adalah kumpulan pasangan nilai-kunci.
- 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.
- Di Kotlin, alat serialisasi data tersedia dalam komponen terpisah, kotlinx.serialization. kotlinx.serialization menyediakan kumpulan library yang mengonversi string JSON menjadi objek Kotlin.
- Ada library Kotlin Pengonversi Serialisasi yang dikembangkan komunitas untuk Retrofit: retrofit2-kotlinx-serialization-converter.
- kotlinx.serialization 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
@SerialName
danvalue
kunci JSON.
11. Pelajari lebih lanjut
Dokumentasi developer Android:
Dokumentasi Kotlin:
- Pengecualian: try, catch, finally, throw, Nothing
- Coroutine, dokumentasi resmi
- Konteks dan dispatcher Coroutine
- Serialisasi | Kotlin
Lainnya: