Sebagian besar aplikasi Android di pasar menghubungkan ke Internet untuk melakukan beberapa operasi jaringan. Misalnya, mengambil email, pesan, atau informasi serupa dari server backend. Gmail, YouTube, dan Google Foto adalah beberapa contoh aplikasi yang terhubung ke internet untuk menampilkan data pengguna.
Dalam codelab ini, Anda menggunakan library yang dikembangkan open source untuk membuat lapisan jaringan dan mendapatkan data dari server backend. Hal ini sangat menyederhanakan pengambilan data, dan juga membantu aplikasi agar sesuai dengan praktik terbaik Android, seperti melakukan operasi di thread latar belakang. Anda juga akan mengupdate antarmuka pengguna aplikasi jika internet lambat atau tidak tersedia; tindakan ini akan terus memberi tahu pengguna tentang masalah konektivitas jaringan.
Yang harus sudah Anda ketahui
- Cara membuat dan menggunakan fragmen.
- Cara menggunakan komponen arsitektur Android
ViewModel
, danLiveData
. - Cara menambahkan dependensi dalam file gradle.
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 objek
LiveData
aplikasi Anda dengan library Moshi. - Gunakan dukungan Retrofit untuk coroutine agar menyederhanakan kode.
Yang Anda perlukan
- Komputer yang dilengkapi Android Studio.
- Kode awal untuk aplikasi MarsPhotos.
Pada jalur ini, Anda bekerja dengan aplikasi awal bernama MarsPhotos, 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. Berikut adalah screenshot aplikasi final, yang berisi petak gambar properti thumbnail, yang dibuat dengan RecyclerView
.
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 foto yang diterima dari server backend dalam tampilan teks:
Mendownload kode awal
Codelab ini menyediakan kode awal bagi Anda untuk diperluas dengan fitur yang dipelajari dalam codelab ini. Kode awal dapat berisi kode yang tidak dikenal dan dikenal bagi Anda dari codelab sebelumnya. Anda akan mempelajari lebih lanjut tentang kode yang tidak dikenal di codelab berikutnya.
Jika Anda menggunakan kode awal dari GitHub, perhatikan bahwa nama foldernya adalah android-basics-kotlin-mars-photos-app
. Pilih folder ini saat Anda membuka project di Android Studio.
Untuk mendapatkan kode yang dipakai pada codelab ini dan membukanya di Android Studio, lakukan hal berikut.
Mendapatkan kode
- Klik URL yang diberikan. Tindakan ini akan membuka halaman GitHub project di browser.
- Di halaman GitHub project, klik tombol Code yang akan menampilkan dialog.
- Di dialog, klik tombol Download ZIP untuk menyimpan project di komputer. Tunggu download selesai.
- Temukan file di komputer Anda (mungkin di folder Downloads).
- Klik dua kali pada file ZIP untuk mengekstraknya. Tindakan ini akan membuat folder baru yang berisi file project.
Membuka project di Android Studio
- Mulai Android Studio.
- Di jendela Welcome to Android Studio, klik Open an existing Android Studio project.
Catatan: Jika Android Studio sudah terbuka, pilih opsi menu File > New > Import Project.
- Di dialog Import Project, buka lokasi folder project yang telah diekstrak (kemungkinan ada di folder Downloads).
- Klik dua kali pada folder project tersebut.
- Tunggu Android Studio membuka project.
- Klik tombol Run untuk membuat dan menjalankan aplikasi. Pastikan aplikasi berfungsi seperti yang diharapkan.
- Cari file project di jendela fitur Project untuk melihat cara aplikasi diterapkan.
Menjalankan kode awal
- Buka project yang telah didownload di Android Studio. Nama folder project adalah
android-basics-kotlin-mars-photos-app
. Struktur folder kode awal akan terlihat seperti di bawah ini. - Di panel Android, luaskan app > java. Perhatikan bahwa aplikasi memiliki folder paket bernama
overview
. Ini adalah lapisan UI aplikasi.
- Jalankan aplikasi. Saat mengompilasi dan menjalankan aplikasi, Anda akan melihat layar berikut dengan teks placeholder di tengah. Pada akhir codelab ini, Anda akan memperbarui teks placeholder ini dengan jumlah foto yang diambil.
- Jelajahi file untuk memahami kode awal. Untuk file tata letak, Anda dapat menggunakan opsi Split di sudut kanan atas untuk melihat pratinjau tata letak dan XML secara bersamaan.
Panduan kode Stater
Dalam tugas ini, Anda akan memahami struktur project. Berikut adalah panduan tentang file dan folder penting dalam project.
OverviewFragment:
- Ini adalah fragmen yang ditampilkan dalam
MainActivity
. Teks placeholder yang Anda lihat di langkah sebelumnya ditampilkan di fragmen ini. - Dalam codelab berikutnya, fragmen ini akan menampilkan data yang diterima dari server backend foto Mars.
- Class ini menyimpan referensi ke objek
OverviewViewModel
. OverviewFragment
memiliki fungsionCreateView()
yang meng-inflate tata letakfragment_overview
menggunakan Data Binding, menetapkan pemilik siklus proses binding kepada dirinya sendiri, dan menetapkan variabelviewModel
dalam objek binding ke pemilik tersebut.- Karena pemilik siklus proses ditetapkan, setiap
LiveData
yang digunakan dalam Data Binding akan otomatis diamati jika ada perubahan, dan UI akan diupdate sebagaimana mestinya.
OverviewViewModel:
- Ini adalah model tampilan yang sesuai untuk
OverviewFragment
. - Class ini berisi properti
MutableLiveData
yang bernama_status
beserta properti pendukungnya. Memperbarui nilai properti ini, memperbarui teks placeholder yang ditampilkan di layar. - Metode
getMarsPhotos()
memperbarui respons placeholder. Nanti di codelab, Anda akan menggunakan ini untuk menampilkan data yang diambil dari server. Sasaran untuk codelab ini adalah untuk memperbaruistatus
LiveData
dalamViewModel
menggunakan data nyata yang Anda dapatkan dari internet.
res/layout/fragment_overview.xml
:
- Tata letak ini disiapkan untuk menggunakan data binding dan terdiri dari satu
TextView
. - Objek ini mendeklarasikan variabel
OverviewViewModel
, lalu mem-bindingstatus
dariViewModel
keTextView
.
MainActivity.kt
: satu-satunya tugas untuk aktivitas ini adalah memuat tata letak aktivitas, activity_main
.
layout/activity_main.xml:
ini adalah tata letak aktivitas utama dengan satu FragmentContainerView
yang mengarah ke fragment_overview
, fragmen ringkasan akan dibuat instance saat aplikasi diluncurkan.
Dalam codelab ini, Anda membuat lapisan untuk layanan jaringan yang berkomunikasi dengan server backend dan mengambil data yang diperlukan. Anda akan menggunakan library pihak ketiga untuk mengimplementasikannya, yang disebut Retrofit. Anda akan mempelajari hal ini lebih lanjut nanti. ViewModel
berkomunikasi langsung dengan lapisan jaringan tersebut, bagian aplikasi lainnya bersifat transparan untuk implementasi ini.
OverviewViewModel
bertanggung jawab membuat panggilan jaringan untuk mendapatkan data foto Mars. Di ViewModel
, Anda menggunakan LiveData
dengan data binding yang memperhatikan siklus proses untuk mengupdate UI aplikasi saat data berubah.
Data foto Mars disimpan di server web. Untuk mendapatkan 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 tanpa status yang umum dikenalREST, yang merupakan kependekan dariRE presentational State Transfer. Layanan web yang menawarkan arsitektur ini dikenal sebagai layanan RESTful.
Permintaan dibuat untuk layanan web RESTful dengan cara standar melalui URI. URI (Uniform Resource Identifier) mengidentifikasi resource di server berdasarkan nama, tanpa menyiratkan lokasi atau cara mengaksesnya. Misalnya, dalam aplikasi untuk pelajaran ini, Anda mengambil url gambar menggunakan URI server berikut (Server ini menghosting real estate Mars dan foto Mars):
android-kotlin-fun-mars-server.appspot.com
Uniform Resource Locator (URL) adalah URI yang menentukan cara bertindak atas atau memperoleh representasi resource, yaitu menentukan mekanisme akses dan lokasi jaringan utamanya.
Contoh:
URL berikut berisi daftar semua 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 akan menggunakan endpoint /photos dalam codelab ini.
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 atau PUT untuk menambahkan/membuat/memperbarui server dengan data baru
- DELETE untuk menghapus data dari server
Aplikasi Anda akan membuat permintaan HTTP GET ke server untuk informasi foto Mars, kemudian server menampilkan respons ke aplikasi kami termasuk url gambar.
Respons dari layanan web biasanya diformat dalam salah satu format web umum seperti format XML atau JSON – format untuk merepresentasikan data terstruktur dalam pasangan nilai kunci. Anda akan mempelajari lebih lanjut tentang JSON di 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 akan 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. Biasanya sebagian besar berupa open source, dikembangkan oleh komunitas, dan dikelola oleh kontribusi kolektif dari komunitas Android yang besar di seluruh dunia. Ini memungkinkan developer Android seperti Anda untuk membuat aplikasi yang lebih baik.
Library Retrofit
Library Retrofit yang akan Anda gunakan dalam codelab ini untuk berbicara dengan layanan web RESTful Mars adalah contoh bagus dari library yang didukung dan dikelola dengan baik. Anda dapat mengetahuinya dengan melihat halaman GitHub, menyelesaikan masalah terbuka (beberapa di antaranya adalah permintaan fitur), dan menyelesaikan masalah. Jika developer mengatasi masalah dan merespons permintaan fitur secara teratur, maka artinya library ini dikelola dengan baik dan merupakan kandidat yang baik untuk digunakan dalam aplikasi. Library tersebut juga memiliki halaman dokumentasi Retrofit.
Library Retrofit akan berkomunikasi dengan backend. Tindakan ini akan membuat URI untuk layanan web berdasarkan parameter yang kita teruskan ke URI. Anda akan melihat lebih banyak tentang hal ini di bagian selanjutnya.
Menambahkan dependensi Retrofit
Android Gradle memungkinkan Anda menambahkan library eksternal ke project Anda. Selain dependensi library, Anda juga harus menyertakan repositori tempat library dihosting. Library Google seperti ViewModel dan LiveData dari library Jetpack dihosting di repositori Google. Sebagian besar library komunitas dihosting di JCenter seperti Retrofit.
- Buka file level
build.gradle(Project: MarsPhotos)
level teratas project. Perhatikan repositori yang tercantum di bawah blokrepositories
. Anda akan melihat dua repositori,google()
,jcenter()
.
repositories {
google()
jcenter()
}
- Buka file gradle level modul,
build.gradle (Module: MarsPhots.app)
. - Di bagian
dependencies
, tambahkan baris berikut untuk library Retrofit:
// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
// Retrofit with Moshi Converter
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
Dependensi pertama adalah untuk library Retrofit2, dan dependensi kedua adalah untuk pelaku konversi skalar Retrofit. Pelaku konversi ini memungkinkan Retrofit menampilkan hasil JSON sebagai String
. Kedua library bekerja bersama.
- Klik Sync Now untuk membuat ulang project dengan dependensi baru.
Menambahkan dukungan untuk fitur bahasa Java 8
Banyak library pihak ketiga termasuk Retrofit2 menggunakan fitur bahasa Java 8. Plugin Android Gradle menyediakan dukungan bawaan untuk menggunakan fitur bahasa Java 8 tertentu.
- Untuk menggunakan fitur bawaan, Anda memerlukan kode berikut dalam file
build.gradle
modul. Langkah ini sudah dilakukan untuk Anda, pastikan kode berikut ada dibuild.gradle(Module: MarsPhotos.app).
Anda
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
Anda akan menggunakan library Retrofit untuk berbicara dengan layanan web Mars dan menampilkan respons JSON mentah sebagai String
. Placeholder TextView
akan 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 pelaku konversi 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 akan menambahkan lapisan jaringan ke project MarsPhotos yang digunakan ViewModel
untuk berkomunikasi dengan layanan web. Anda akan menerapkan API layanan Retrofit, dengan langkah-langkah berikut.
- Buat lapisan jaringan, class
MarsApiService
. - Buat objek Retrofit dengan URL dasar dan factory pelaku konversi.
- Buat antarmuka yang menjelaskan cara Retrofit berbicara ke server web kami.
- Buat layanan Retrofit dan mengekspos instance ke layanan API ke seluruh aplikasi.
Terapkan langkah-langkah di atas:
- Buat paket baru yang disebut jaringan. Di panel project Android, klik kanan paket,
com.example.android.marsphotos
. Pilih New > Package. Di pop-up, tambahkan network ke akhir nama paket yang disarankan. - Buat file Kotlin baru di bawah network 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"
- Tepat di bawah konstanta itu, tambahkan builder Retrofit untuk membuat objek Retrofit.
private val retrofit = Retrofit.Builder()
Impor retrofit2.Retrofit
, bila diminta.
- Retrofit memerlukan URI dasar untuk layanan web, dan factory pelaku konversi untuk membuat API layanan web. Pelaku konversi 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 memilikiScalarsConverter
yang mendukung string dan jenis sederhana lainnya, jadi Anda memanggiladdConverterFactory()
pada builder dengan instanceScalarsConverterFactory
.
private val retrofit = Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
Impor retrofit2.converter.scalars.ScalarsConverterFactory
bila diminta.
- Tambahkan URI dasar untuk layanan web menggunakan metode
baseUrl()
. Terakhir, panggilbuild()
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 berbicara ke server web menggunakan permintaan HTTP.
interface MarsApiService {
}
- Di dalam antarmuka
MarsApiService
, tambahkan fungsi yang bernamagetPhotos()
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 hal ini endpoint disebutphotos
. Seperti yang disebutkan di tugas sebelumnya, Anda akan menggunakan endpoint /photos dalam codelab ini.
interface MarsApiService {
@GET("photos")
fun getPhotos()
}
Impor retrofit2.http.GET
bila diminta.
- Saat metode
getPhotos()
dipanggil, Retrofit menambahkan endpointphotos
ke URL dasar (yang Anda tentukan dalam builder Retrofit) yang digunakan untuk memulai permintaan. Tambahkan jenis nilai yang ditampilkan fungsi keString
.
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, memiliki satu titik akses global ke objek tersebut. Inisialisasi deklarasi objek adalah thread-safe dan dilakukan pada akses pertama.
Kotlin memudahkan untuk mendeklarasikan singleton. Berikut adalah contoh deklarasi objek dan aksesnya. Deklarasi objek selalu memiliki nama yang mengikuti kata kunci object
.
Contoh:
// Object declaration
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}
val allDataProviders: Collection<DataProvider>
get() = // ...
}
// To refer to the object, use its name directly.
DataProviderManager.registerDataProvider(...)
Panggilan untuk membuat() fungsi pada objek Retrofit sangat mahal dan aplikasi hanya memerlukan satu instance layanan API Retrofit. Jadi, Anda mengekspos layanan ke seluruh aplikasi menggunakan deklarasi objek.
- Di luar deklarasi antarmuka
MarsApiService
, tentukan objek publik yang disebutMarsApi
untuk menginisialisasi layanan Retrofit. Ini adalah objek singleton publik yang dapat diakses dari 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. Anda akan memperbaiki error 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 selanjutnya, Anda akan menggunakan objek Retrofit yang telah diimplementasikan.
Memanggil layanan web di OverviewViewModel
Pada langkah ini, Anda akan mengimplementasikan metode getMarsPhotos()
yang memanggil layanan retrofit, 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 akan menggunakan ViewModelScope
untuk meluncurkan coroutine dan melakukan panggilan jaringan Retrofit di latar belakang.
- Di
MarsApiService
, jadikangetPhotos()
sebagai fungsi penangguhan. Sehingga Anda dapat memanggil metode ini dari dalam coroutine.
@GET("photos")
suspend fun getPhotos(): String
- Buka
overview/OverviewViewModel
. Scroll ke bawah, ke metodegetMarsPhotos()
. Hapus baris yang menetapkan respons status ke"Set the Mars API Response here!".
MetodegetMarsPhotos()
harus kosong sekarang.
private fun getMarsPhotos() {
}
- Di dalam
getMarsPhotos()
, luncurkan coroutine menggunakanviewModelScope.launch
.
private fun getMarsPhotos() {
viewModelScope.launch {
}
}
Impor androidx.lifecycle.
viewModelScope
dan kotlinx.coroutines.launch
bila diminta.
- Di dalam
viewModelScope
, gunakan objek singletonMarsApi
, untuk memanggil metodegetPhotos()
dari antarmukaretrofitService
. Simpan respons yang ditampilkan dalamval
yang bernamalistResult
.
viewModelScope.launch {
val listResult = MarsApi.retrofitService.getPhotos()
}
Impor com.example.android.marsphotos.network.MarsApi
bila diminta.
- Tetapkan hasil yang baru saja kami terima dari server backend ke
_status.value.
val listResult = MarsApi.retrofitService.getProperties()
_status.value = listResult
- Jalankan aplikasi, perhatikan bahwa aplikasi segera ditutup, mungkin menampilkan atau tidak menampilkan pop-up error.
- 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
. Anda akan mengatasi hal ini dengan menambahkan izin internet ke aplikasi pada tugas berikutnya.
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. Ini dianggap sebagai izin yang wajar. Untuk mempelajari lebih lanjut tentang izin Android dan jenisnya, lihat dokumentasi.
Pada langkah ini, aplikasi Anda mendeklarasikan izin yang diperlukan dengan menyertakan tag <uses-permission>
dalam file AndroidManifest
.
- Buka
manifests/AndroidManifest.xml
. Tambahkan baris ini sebelum tag<application>
:
<uses-permission android:name="android.permission.INTERNET" />
- Kompilasi dan jalankan aplikasi lagi. Jika Anda memiliki koneksi internet yang aktif, Anda akan melihat teks JSON yang berisi data terkait foto Mars. Anda akan mempelajari lebih lanjut tentang format JSON nanti dalam codelab.
- Ketuk tombol Kembali di perangkat atau emulator untuk menutup aplikasi.
- Setel perangkat atau emulator ke mode pesawat untuk menyimulasikan error koneksi jaringan. Buka kembali aplikasi dari menu terbaru, atau mulai ulang aplikasi dari Android Studio.
- Klik tab Logcat di Android Studio dan catat pengecualian fatal dalam log, yang terlihat seperti ini:
3302-3302/com.example.android.marsphotos E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.android.marsphotos, PID: 3302 java.net.SocketTimeoutException: timeout ...
Pesan error ini menunjukkan aplikasi mencoba untuk terhubung dan waktu habis. Pengecualian seperti ini sangat umum terjadi secara real time. Pada langkah berikutnya, Anda akan mempelajari cara menangani pengecualian tersebut.
Penanganan Pengecualian
Pengecualian adalah error yang dapat terjadi selama waktu proses(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 ditangani dengan cara yang mudah digunakan.
Alasan pengecualian bisa sesederhana pembagian dengan nol atau error dalam jaringan. Pengecualian ini mirip dengan NumberFormatException
yang Anda pelajari di codelab sebelumnya.
Contoh potensi masalah saat menghubungkan ke server:
- 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 diambil selama waktu kompilasi. Anda dapat menggunakan blok try-catch
untuk menangani pengecualian dalam waktu proses. Untuk mempelajari lebih lanjut, baca dokumentasi.
Contoh sintaks 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 menjalankan kode tempat Anda mengantisipasi pengecualian, dalam aplikasi ini akan menjadi panggilan jaringan. Pada blok catch
, Anda akan menerapkan kode yang mencegah penghentian aplikasi secara tiba-tiba. Jika ada pengecualian, blok catch
akan dijalankan untuk memulihkan error, bukan menghentikan aplikasi secara tiba-tiba.
- Buka
overview/OverviewViewModel.kt
. Scroll ke bawah, ke metodegetMarsPhotos()
. Di dalam blok peluncuran, tambahkan bloktry
di sekitar panggilanMarsApi
untuk menangani pengecualian. Tambahkan blokcatch
setelah bloktry
:
viewModelScope.launch {
try {
val listResult = MarsApi.retrofitService.getPhotos()
_status.value = listResult
} catch (e: Exception) {
}
}
- Di dalam blok
catch {}
, tangani respons kegagalan. Tampilkan pesan error kepada pengguna dengan menyetele.message
ke_status.
value
.
catch (e: Exception) {
_status.value = "Failure: ${e.message}"
}
- Jalankan aplikasi lagi dengan mengaktifkan mode pesawat. Aplikasi tidak menutup sendiri sekarang, tetapi menampilkan pesan error.
- Nonaktifkan mode pesawat di ponsel atau emulator. Jalankan dan uji aplikasi Anda, pastikan semuanya berfungsi dengan baik dan Anda dapat melihat string JSON.
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 akan mengambil data dari server ini: https:// android-kotlin-fun-mars-server.appspot.com/photos. Jika Anda memasukkan URL ini di browser,
Anda akan melihat daftar ID dan URL gambar permukaan Mars dalam format JSON!
Struktur respons JSON sampel:
- 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 pasangan nama-nilai. Nama dan nilai dipisahkan dengan titik dua.
- 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. Jika Anda menempelkan URL ke browser web, Anda akan melihat gambar permukaan Mars.
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 eksternal bernama Moshi, yang merupakan parser JSON Android yang mengubah string JSON menjadi objek Kotlin. Retrofit memiliki pelaku konversi 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 berguna yang menampilkan foto Mars. Anda akan mengubah aplikasi sehingga tidak menampilkan JSON mentah, aplikasi akan menampilkan jumlah foto Mars yang dikembalikan.
Menambahkan dependensi library Moshi
- Buka build.gradle (Module: app).
- Di bagian dependensi, tambahkan kode yang ditampilkan di bawah untuk menyertakan dependensi Moshi. Dependensi ini menambahkan dukungan untuk library JSON Moshi dengan dukungan Kotlin.
// Moshi
implementation 'com.squareup.moshi:moshi-kotlin:1.9.3'
- Temukan baris untuk pelaku konversi skalar Retrofit di blok
dependencies
dan ubah dependensi ini untuk menggunakanconverter-moshi
:
Ganti ini
// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
// Retrofit with Moshi Converter
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
dengan ini
// Retrofit with Moshi Converter
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
- Klik Sync Now untuk membuat ulang project dengan dependensi baru.
Menerapkan class data Mars Photos
Contoh entri respons JSON yang Anda dapatkan dari layanan web terlihat seperti ini, 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 disertakan dalam" "
, jenis file tersebut adalahString
bukanInteger
.img_src
: URL gambar sebagai string.
Moshi mengurai data JSON ini dan mengubahnya menjadi objek Kotlin. Untuk melakukannya, Moshi perlu memiliki class data Kotlin untuk menyimpan hasil yang diuraikan, sehingga pada langkah ini Anda akan membuat class data, MarsPhoto
.
- Klik kanan pada paket network dan pilih New > Kotlin File/Class.
- Di pop-up, pilih Class dan masukkan
MarsPhoto
sebagai nama class. Tindakan ini akan membuat file baru bernamaMarsPhoto.kt
dalam paketnetwork
. - Buat
MarsPhoto
class data dengan menambahkan kata kuncidata
sebelum definisi class. Mengubah tanda kurung {} menjadi kurung (). Hal 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
)
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 Moshi mengurai JSON, Moshi akan mencocokkan kunci berdasarkan nama dan mengisi objek data dengan nilai yang sesuai.
Anotasi @Json
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 @Json
. Dalam contoh ini, nama variabel di class data adalah imgSrcUrl
. Variabel dapat dipetakan ke atribut JSON img_src
menggunakan @Json(name = "img_src")
.
- Ganti baris untuk kunci
img_src
dengan baris yang ditampilkan di bawah. Imporcom.squareup.moshi.Json
bila diminta.
@Json(name = "img_src") val imgSrcUrl: String
Mengupdate MarsApiService dan OverviewViewModel
Dalam tugas ini, Anda akan membuat objek Moshi menggunakan Builder Moshi, mirip dengan builder Retrofit.
Anda akan mengganti ScalarsConverterFactory
dengan KotlinJsonAdapterFactory
untuk memberi tahu Retrofit bahwa Moshi dapat digunakan untuk mengonversi respons JSON menjadi objek Kotlin. Anda kemudian akan memperbarui API jaringan dan ViewModel
untuk menggunakan objek Moshi.
- Buka
network/MarsApiService.kt
. Perhatikan error referensi yang belum terselesaikan untukScalarsConverterFactory
. Hal ini terjadi karena perubahan dependensi Retrofit yang Anda buat di langkah sebelumnya. Hapus impor untukScalarConverterFactory
. Anda akan segera memperbaiki error lainnya.
Hapus:
import retrofit2.converter.scalars.ScalarsConverterFactory
- Di bagian atas file, tepat sebelum builder Retrofit, tambahkan kode berikut untuk membuat objek Moshi, mirip dengan objek Retrofit.
private val moshi = Moshi.Builder()
Impor com.squareup.moshi.Moshi
dan com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
bila diminta.
- Agar anotasi Moshi dapat berfungsi dengan baik dengan Kotlin, di builder Moshi, tambahkan
KotlinJsonAdapterFactory
, lalu panggilbuild()
.
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
- Pada deklarasi objek
retrofit
, ubah builder Retrofit untuk menggunakanMoshiConverterFactory
, bukanScalarConverterFactory
, dan meneruskan instancemoshi
yang baru saja Anda buat.
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
Impor retrofit2.converter.moshi.MoshiConverterFactory
bila diminta.
- Setelah menempatkan
MoshiConverterFactory
, Anda dapat meminta Retrofit untuk menampilkan daftar objekMarsPhoto
dari array JSON, bukan menampilkan string JSON. Update antarmukaMarsApiService
agar Retrofit menampilkan daftar objekMarsPhoto
, bukan menampilkanString
.
interface MarsApiService {
@GET("photo")
fun getPhotos(): List<MarsPhoto>
}
- Lakukan perubahan yang mirip dengan
viewModel
, bukaOverviewViewModel.kt
. Scroll ke bawah ke metodegetMarsPhotos()
. - Dalam metode
getMarsPhotos()
,listResult
adalahList<MarsPhoto>
bukanString
lagi. Ukuran daftar tersebut adalah jumlah foto yang diterima dan diuraikan. Untuk mencetak jumlah foto, ambil pembaruan_status.
value
sebagai berikut.
_status.value = "Success: ${listResult.size} Mars photos retrieved"
Impor com.example.android.marsphotos.network.MarsPhoto
bila diminta.
- Pastikan mode pesawat dinonaktifkan pada perangkat atau emulator. Kompilasi dan jalankan aplikasi. Kali ini pesan akan menampilkan jumlah properti yang ditampilkan dari layanan web, bukan string JSON besar:
build.gradle(Module : MarsPhotos.app)
Inilah dependensi baru yang akan disertakan.
dependencies { ... // Moshi implementation 'com.squareup.moshi:moshi-kotlin:1.9.3' // Retrofit with Moshi Converter implementation 'com.squareup.retrofit2:converter-moshi:2.9.0' ... }
Manifests/AndroidManifest.xml
Tambahkan izin internet, kode <uses-permission..>
dari cuplikan kode di bawah.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.marsphotos">
<!-- In order for our app to access the Internet, we need to define this permission. -->
<uses-permission android:name="android.permission.INTERNET" />
<application
...
</application>
</manifest>
network/MarsPhoto.kt
package com.example.android.marsphotos.network
import com.squareup.moshi.Json
/**
* This data class defines a Mars photo which includes an ID, and the image URL.
* The property names of this data class are used by Moshi to match the names of values in JSON.
*/
data class MarsPhoto(
val id: String,
@Json(name = "img_src") val imgSrcUrl: String
)
network/MarsApiService.kt
package com.example.android.marsphotos.network
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
private const val BASE_URL =
"https://android-kotlin-fun-mars-server.appspot.com"
/**
* Build the Moshi object with Kotlin adapter factory that Retrofit will be using.
*/
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
/**
* The Retrofit object with the Moshi converter.
*/
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
/**
* A public interface that exposes the [getPhotos] method
*/
interface MarsApiService {
/**
* Returns a [List] of [MarsPhoto] and this method can be called from a Coroutine.
* The @GET annotation indicates that the "photos" endpoint will be requested with the GET
* HTTP method
*/
@GET("photos")
suspend fun getPhotos() : List<MarsPhoto>
}
/**
* A public Api object that exposes the lazy-initialized Retrofit service
*/
object MarsApi {
val retrofitService: MarsApiService by lazy { retrofit.create(MarsApiService::class.java) }
}
Overview/OverviewViewModel.kt
package com.example.android.marsphotos.overview
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.android.marsphotos.network.MarsApi
import kotlinx.coroutines.launch
/**
* The [ViewModel] that is attached to the [OverviewFragment].
*/
class OverviewViewModel : ViewModel() {
// The internal MutableLiveData that stores the status of the most recent request
private val _status = MutableLiveData<String>()
// The external immutable LiveData for the request status
val status: LiveData<String> = _status
/**
* Call getMarsPhotos() on init so we can display status immediately.
*/
init {
getMarsPhotos()
}
/**
* Gets Mars photos information from the Mars API Retrofit service and updates the
* [MarsPhoto] [List] [LiveData].
*/
private fun getMarsPhotos() {
viewModelScope.launch {
try {
val listResult = MarsApi.retrofitService.getPhotos()
_status.value = "Success: ${listResult.size} Mars photos retrieved"
} catch (e: Exception) {
_status.value = "Failure: ${e.message}"
}
}
}
}
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 pelaku konversi untuk memberi tahu Retrofit apa yang harus dilakukan dengan data yang dikirimnya ke layanan web dan didapatkan kembali dari layanan web. Misalnya, pelaku konversi
ScalarsConverter
memperlakukan data layanan web sebagaiString
atau hal sederhana lainnya. - Agar aplikasi Anda dapat terhubung ke internet, tambahkan izin
"android.permission.INTERNET"
dalam manifes Android.
Penguraian JSON
- Respons dari layanan web sering diformat dalam JSON, format umum untuk menampilkan 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.
- Library Moshi adalah parser JSON Android yang mengonversi string JSON menjadi objek Kotlin. Retrofit memiliki pelaku konversi 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.
Dokumentasi developer Android:
Dokumentasi Kotlin:
- Pengecualian: try, catch, finally, throw, Nothing
- Codelab Coroutine
- Coroutine, dokumentasi resmi
- konteks dan petugas operator Coroutine
Lainnya: