Mendapatkan data dari internet

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, dan LiveData.
  • 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.

6c26142b52c51285.png

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:

e98a0641540fcb73.png

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

  1. Klik URL yang diberikan. Tindakan ini akan membuka halaman GitHub project di browser.
  2. Di halaman GitHub project, klik tombol Code yang akan menampilkan dialog.

Eme2bJP46u-pMpnXVfm-bS2N2dlyq6c0jn1DtQYqBaml7TUhzXDWpYoDI0lGKi4xndE_uJw8sKfwfOZ1fC503xCVZrbh10JKJ4iEHdLDwFfdvnOheNxkokITW1LW6UZTncVJJUZ5Fw

  1. Di dialog, klik tombol Download ZIP untuk menyimpan project di komputer. Tunggu download selesai.
  2. Temukan file di komputer Anda (mungkin di folder Downloads).
  3. Klik dua kali pada file ZIP untuk mengekstraknya. Tindakan ini akan membuat folder baru yang berisi file project.

Membuka project di Android Studio

  1. Mulai Android Studio.
  2. Di jendela Welcome to Android Studio, klik Open an existing Android Studio project.

Tdjf5eS2nCikM9KdHgFaZNSbIUCzKXP6WfEaKVE2Oz1XIGZhgTJYlaNtXTHPFU1xC9pPiaD-XOPdIxVxwZAK8onA7eJyCXz2Km24B_8rpEVI_Po5qlcMNN8s4Tkt6kHEXdLQTDW7mg

Catatan: Jika Android Studio sudah terbuka, pilih opsi menu File > New > Import Project.

PaMkVnfCxQqSNB1LxPpC6C6cuVCAc8jWNZCqy5tDVA6IO3NE2fqrfJ6p6ggGpk7jd27ybXaWU7rGNOFi6CvtMyHtWdhNzdAHmndzvEdwshF_SG24Le01z7925JsFa47qa-Q19t3RxQ

  1. Di dialog Import Project, buka lokasi folder project yang telah diekstrak (kemungkinan ada di folder Downloads).
  2. Klik dua kali pada folder project tersebut.
  3. Tunggu Android Studio membuka project.
  4. Klik tombol Run j7ptomO2PEQNe8jFt4nKCOw_Oc_Aucgf4l_La8fGLCMLy0t9RN9SkmBFGOFjkEzlX4ce2w2NWq4J30sDaxEe4MaSNuJPpMgHxnsRYoBtIV3-GUpYYcIvRJ2HrqR27XGuTS4F7lKCzg untuk membuat dan menjalankan aplikasi. Pastikan aplikasi berfungsi seperti yang diharapkan.
  5. Cari file project di jendela fitur Project untuk melihat cara aplikasi diterapkan.

Menjalankan kode awal

  1. 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.
  2. Di panel Android, luaskan app > java. Perhatikan bahwa aplikasi memiliki folder paket bernama overview. Ini adalah lapisan UI aplikasi.

39027f91862361f1.png

  1. 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.

4886b6b36023a53f.png

  1. 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 fungsi onCreateView() yang meng-inflate tata letak fragment_overview menggunakan Data Binding, menetapkan pemilik siklus proses binding kepada dirinya sendiri, dan menetapkan variabel viewModel 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 memperbarui status LiveData dalam ViewModel 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-binding status dari ViewModel ke TextView.

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.

d5a05ab8fd5ff011.png

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.

b3ab0ee52bfd791e.png

4a23a1ba3307b2a5.png

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.

9fb57e255df97a4d.png

6da405d572445df9.png

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.

c9e1034e86327abd.png

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.

  1. Buka file level build.gradle(Project: MarsPhotos) level teratas project. Perhatikan repositori yang tercantum di bawah blok repositories. Anda akan melihat dua repositori, google(), jcenter().
repositories {
   google()
   jcenter()
}
  1. Buka file gradle level modul, build.gradle (Module: MarsPhots.app).
  2. 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.

  1. 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.

  1. Untuk menggunakan fitur bawaan, Anda memerlukan kode berikut dalam file build.gradle modul. Langkah ini sudah dilakukan untuk Anda, pastikan kode berikut ada di build.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.

64fe0b5717a31f71.png

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:

  1. 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.
  2. Buat file Kotlin baru di bawah network paket baru. Beri nama MarsApiService.
  3. 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"
  1. Tepat di bawah konstanta itu, tambahkan builder Retrofit untuk membuat objek Retrofit.
private val retrofit = Retrofit.Builder()

Impor retrofit2.Retrofit, bila diminta.

  1. 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 memiliki ScalarsConverter yang mendukung string dan jenis sederhana lainnya, jadi Anda memanggil addConverterFactory() pada builder dengan instance ScalarsConverterFactory.
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())

Impor retrofit2.converter.scalars.ScalarsConverterFactory bila diminta.

  1. Tambahkan URI dasar untuk layanan web menggunakan metode baseUrl(). Terakhir, panggil build() untuk membuat objek Retrofit.
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())
   .baseUrl(BASE_URL)
   .build()
  1. Pada panggilan ke builder Retrofit, tentukan antarmuka yang disebut MarsApiService, yang menentukan cara Retrofit berbicara ke server web menggunakan permintaan HTTP.
interface MarsApiService {
}
  1. Di dalam antarmuka MarsApiService, tambahkan fungsi yang bernama getPhotos() untuk mendapatkan string respons dari layanan web.
interface MarsApiService {
    fun getPhotos()
}
  1. Gunakan anotasi @GET untuk memberi tahu Retrofit bahwa ini adalah permintaan GET, dan tentukan endpoint, untuk metode layanan web tersebut. Dalam hal ini endpoint disebut photos. 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.

  1. 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, 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.

  1. Di luar deklarasi antarmuka MarsApiService, tentukan objek publik yang disebut MarsApi untuk menginisialisasi layanan Retrofit. Ini adalah objek singleton publik yang dapat diakses dari aplikasi lainnya.
object MarsApi {

}
  1. Di dalam deklarasi objek MarsApi, tambahkan properti objek retrofit yang diinisialisasi dengan lambat bernama retrofitService dari jenis MarsApiService. 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 {
       }
}
  1. Inisialisasi variabel retrofitService menggunakan metode retrofit.create() dengan antarmuka MarsApiService.
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.

  1. Di MarsApiService, jadikan getPhotos() sebagai fungsi penangguhan. Sehingga Anda dapat memanggil metode ini dari dalam coroutine.
@GET("photos")
suspend fun getPhotos(): String
  1. Buka overview/OverviewViewModel. Scroll ke bawah, ke metode getMarsPhotos(). Hapus baris yang menetapkan respons status ke "Set the Mars API Response here!". Metode getMarsPhotos() harus kosong sekarang.
private fun getMarsPhotos() {

}
  1. Di dalam getMarsPhotos(), luncurkan coroutine menggunakan viewModelScope.launch.
private fun getMarsPhotos() {
    viewModelScope.launch {
    }
}

Impor androidx.lifecycle.viewModelScope dan kotlinx.coroutines.launch bila diminta.

  1. Di dalam viewModelScope, gunakan objek singleton MarsApi, untuk memanggil metode getPhotos() dari antarmuka retrofitService. Simpan respons yang ditampilkan dalam val yang bernama listResult.
viewModelScope.launch {
    val listResult = MarsApi.retrofitService.getPhotos()
}

Impor com.example.android.marsphotos.network.MarsApi bila diminta.

  1. Tetapkan hasil yang baru saja kami terima dari server backend ke _status.value.
 val listResult = MarsApi.retrofitService.getProperties()
 _status.value = listResult
  1. Jalankan aplikasi, perhatikan bahwa aplikasi segera ditutup, mungkin menampilkan atau tidak menampilkan pop-up error.
  2. 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.

  1. Buka manifests/AndroidManifest.xml. Tambahkan baris ini sebelum tag <application>:
<uses-permission android:name="android.permission.INTERNET" />
  1. 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.

f7ba3feaf864d4cf.png

  1. Ketuk tombol Kembali di perangkat atau emulator untuk menutup aplikasi.
  2. 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.
  3. 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.

  1. Buka overview/OverviewViewModel.kt. Scroll ke bawah, ke metode getMarsPhotos(). Di dalam blok peluncuran, tambahkan blok try di sekitar panggilan MarsApi untuk menangani pengecualian. Tambahkan blok catch setelah blok try:
viewModelScope.launch {
   try {
       val listResult = MarsApi.retrofitService.getPhotos()
       _status.value = listResult
   } catch (e: Exception) {

   }
}
  1. Di dalam blok catch {}, tangani respons kegagalan. Tampilkan pesan error kepada pengguna dengan menyetel e.message ke _status.value.
catch (e: Exception) {
   _status.value = "Failure: ${e.message}"
}
  1. Jalankan aplikasi lagi dengan mengaktifkan mode pesawat. Aplikasi tidak menutup sendiri sekarang, tetapi menampilkan pesan error.

2fbc318b4fff2f34.png

  1. 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:

68fdfa54410ee03e.png

  • 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.

17116bdeb21fec0d.png

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

  1. Buka build.gradle (Module: app).
  2. 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'
  1. Temukan baris untuk pelaku konversi skalar Retrofit di blok dependencies dan ubah dependensi ini untuk menggunakan converter-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'
  1. 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 adalah String bukan Integer.
  • 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.

  1. Klik kanan pada paket network dan pilih New > Kotlin File/Class.
  2. Di pop-up, pilih Class dan masukkan MarsPhoto sebagai nama class. Tindakan ini akan membuat file baru bernama MarsPhoto.kt dalam paket network.
  3. Buat MarsPhoto class data dengan menambahkan kata kunci data 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(
)
  1. 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").

  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

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.

  1. Buka network/MarsApiService.kt. Perhatikan error referensi yang belum terselesaikan untuk ScalarsConverterFactory. Hal ini terjadi karena perubahan dependensi Retrofit yang Anda buat di langkah sebelumnya. Hapus impor untuk ScalarConverterFactory. Anda akan segera memperbaiki error lainnya.

Hapus:

import retrofit2.converter.scalars.ScalarsConverterFactory
  1. 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.

  1. Agar anotasi Moshi dapat berfungsi dengan baik dengan Kotlin, di builder Moshi, tambahkan KotlinJsonAdapterFactory, lalu panggil build().
private val moshi = Moshi.Builder()
   .add(KotlinJsonAdapterFactory())
   .build()
  1. Pada deklarasi objek retrofit, ubah builder Retrofit untuk menggunakan MoshiConverterFactory, bukan ScalarConverterFactory, dan meneruskan instance moshi 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.

  1. Setelah menempatkan MoshiConverterFactory, Anda dapat meminta Retrofit untuk menampilkan daftar objek MarsPhoto dari array JSON, bukan menampilkan string JSON. Update antarmuka MarsApiService agar Retrofit menampilkan daftar objek MarsPhoto, bukan menampilkan String.
interface MarsApiService {
   @GET("photo")
   fun getPhotos(): List<MarsPhoto>
}
  1. Lakukan perubahan yang mirip dengan viewModel, buka OverviewViewModel.kt. Scroll ke bawah ke metode getMarsPhotos().
  2. Dalam metode getMarsPhotos(), listResult adalah List<MarsPhoto> bukan String 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.

  1. 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:

7da53c64bd36fe74.png

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 sebagai String 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:

Lainnya: