Dasar-Dasar Cronet

1. Pengantar

1ee223bf9e1b75fb.png

Terakhir Diperbarui: 06-05-2022

Cronet adalah stack jaringan Chromium yang disediakan untuk aplikasi Android sebagai library. Cronet memanfaatkan beberapa teknologi yang mampu mengurangi latensi dan meningkatkan throughput permintaan jaringan yang diperlukan aplikasi agar dapat berfungsi.

Library Cronet menangani permintaan aplikasi yang digunakan oleh jutaan orang setiap harinya, seperti YouTube, Aplikasi Google, Google Foto, serta Maps - Navigasi & Transit. Cronet adalah library jaringan Android yang paling sering digunakan dengan dukungan HTTP3.

Untuk mengetahui detail selengkapnya, lihat halaman Fitur Cronet.

Yang akan Anda build

Dalam codelab ini, Anda akan menambahkan dukungan Cronet ke aplikasi tampilan gambar. Aplikasi Anda akan:

  • Memuat Cronet dari Layanan Google Play, atau di-fallback dengan aman jika Cronet tidak tersedia.
  • Mengirim permintaan serta menerima dan memproses respons menggunakan Cronet.
  • Menampilkan hasil dalam UI sederhana.

28b0fcb0fed5d3e0.png

Yang akan Anda pelajari

  • Cara menyertakan Cronet sebagai dependensi ke aplikasi Anda
  • Cara mengonfigurasi mesin Cronet
  • Cara menggunakan Cronet untuk mengirim permintaan
  • Cara menulis callback Cronet untuk memproses respons

Codelab ini berfokus pada penggunaan Cronet. Sebagian besar aplikasi sudah diimplementasikan sebelumnya dan Anda akan dapat menyelesaikan codelab tanpa perlu pengetahuan mendalam terkait pengembangan Android. Meskipun demikian, untuk mengoptimalkan codelab ini, Anda harus memahami dasar-dasar pengembangan Android dan library Jetpack Compose.

Yang akan Anda butuhkan

2. Mendapatkan kode

Kami telah memasukkan semua yang Anda perlukan untuk project ini ke dalam repo Git. Untuk memulai, clone repositori dan buka kode di Android Studio.

git clone https://github.com/android/codelab-cronet-basics

3. Menetapkan dasar pengukuran

Apa yang menjadi titik awal?

Titik awal kita adalah aplikasi tampilan gambar dasar yang dirancang untuk codelab ini. Jika mengklik tombol Add an image, Anda akan melihat bahwa gambar baru ditambahkan ke daftar bersama detail waktu yang diperlukan untuk mengambil gambar dari internet. Aplikasi ini menggunakan library HTTP bawaan yang disediakan oleh Kotlin yang tidak mendukung fitur lanjutan apa pun.

Sepanjang codelab ini, kita akan memperluas aplikasi agar dapat menggunakan Cronet dan beberapa fiturnya.

4. Menambahkan dependensi ke skrip Gradle

Anda dapat mengintegrasikan Cronet sebagai library mandiri yang disertakan bersama aplikasi Anda, atau menggunakan Cronet sebagaimana yang disediakan oleh platform. Tim Cronet merekomendasikan penggunaan penyedia Layanan Google Play. Dengan menggunakan penyedia Layanan Google Play, aplikasi Anda tidak perlu membayar biaya ukuran biner untuk menyertakan Cronet (sekitar 5 megabyte) dan platform memastikan bahwa update dan perbaikan keamanan terbaru akan dikirimkan.

Terlepas dari pilihan Anda untuk mengimpor implementasi, Anda juga perlu menambahkan dependensi cronet-api untuk menyertakan Cronet API.

Buka file build.gradle, lalu tambahkan dua baris berikut ke bagian dependencies.

implementation 'com.google.android.gms:play-services-cronet:18.0.1'
implementation 'org.chromium.net:cronet-api:101.4951.41'

5. Menginstal penyedia Cronet Layanan Google Play

Seperti yang telah dibahas di bagian sebelumnya, Cronet dapat ditambahkan ke aplikasi Anda dengan beberapa cara. Masing-masing cara ini diabstraksi oleh Provider, yang memastikan bahwa link yang diperlukan antara library dan aplikasi Anda sudah ada. Setiap kali Anda membuat mesin Cronet baru, Cronet melihat semua penyedia yang aktif dan memilih penyedia terbaik untuk membuat instance mesin.

Penyedia Layanan Google Play biasanya tidak langsung tersedia sehingga Anda harus menginstalnya terlebih dahulu. Cari TODO dalam MainActivity dan tempel cuplikan berikut:

val ctx = LocalContext.current
CronetProviderInstaller.installProvider(ctx)

Tindakan ini akan meluncurkan Tugas Layanan Play yang menginstal penyedia secara asinkron.

6. Menangani hasil penginstalan penyedia

Anda telah berhasil menginstal penyedia... Tunggu, apa benar? Task bersifat asinkron dan Anda belum menangani hasilnya dengan cara apa pun. Ayo perbaiki. Ganti pemanggilan installProvider dengan cuplikan berikut:

CronetProviderInstaller.installProvider(ctx).addOnCompleteListener {
   if (it.isSuccessful) {
       Log.i(LOGGER_TAG, "Successfully installed Play Services provider: $it")
       // TODO(you): Initialize Cronet engine
   } else {
       Log.w(LOGGER_TAG, "Unable to load Cronet from Play Services", it.exception)
   }
}

Untuk tujuan codelab ini, kita akan terus menggunakan downloader gambar native jika pemuatan Cronet gagal. Jika performa jaringan sangat penting bagi aplikasi, Anda mungkin ingin menginstal atau mengupdate Layanan Play. Untuk detail selengkapnya, lihat dokumentasi CronetProviderInstaller.

Jalankan aplikasi sekarang. Jika semuanya berfungsi dengan baik, Anda akan melihat laporan log bahwa penyedia berhasil diinstal.

7. Membuat mesin Cronet

Mesin Cronet adalah objek core yang akan Anda gunakan untuk mengirim permintaan dengan Cronet. Mesin ini dibuat menggunakan Pola builder, yang memungkinkan Anda mengonfigurasi berbagai opsi Cronet. Untuk saat ini, kita akan terus menggunakan opsi default. Buat instance mesin Cronet baru dengan mengganti TODO dengan cuplikan berikut:

val cronetEngine = CronetEngine.Builder(ctx).build()
// TODO(you): Initialize the Cronet image downloader

8. Mengimplementasikan callback Cronet

Sifat asinkron Cronet berarti penanganan respons dikontrol menggunakan callback, yaitu, instance UrlRequest.Callback. Di bagian ini, Anda akan mengimplementasikan callback helper yang membaca seluruh respons ke memori.

Buat class abstrak baru yang disebut ReadToMemoryCronetCallback, buat agar class abstrak tersebut memperluas UrlRequest.Callback, dan biarkan Android Studio otomatis membuat stub metode. Class baru Anda akan terlihat mirip dengan cuplikan berikut:

abstract class ReadToMemoryCronetCallback : UrlRequest.Callback() {
   override fun onRedirectReceived(
       request: UrlRequest,
       info: UrlResponseInfo,
       newLocationUrl: String?
   ) {
       TODO("Not yet implemented")
   }

   override fun onSucceeded(request: UrlRequest, info: UrlResponseInfo) {
       TODO("Not yet implemented")
   }

   override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
       TODO("Not yet implemented")
   }

   override fun onResponseStarted(request: UrlRequest, info: UrlResponseInfo) {
       TODO("Not yet implemented")
   }

   override fun onReadCompleted(
       request: UrlRequest,
       info: UrlResponseInfo,
       byteBuffer: ByteBuffer
   ) {
       TODO("Not yet implemented")
   }
}

Metode onRedirectReceived, onSucceeded, dan onFailed sudah jelas, jadi kita tidak akan membahas detailnya sekarang, dan kita akan fokus pada onResponseStarted dan onReadCompleted.

onResponseStarted dipanggil setelah Cronet mengirim permintaan dan menerima semua header respons, tetapi sebelum mulai membaca isi. Cronet tidak otomatis membaca seluruh isi seperti beberapa library lainnya (misalnya Volley). Sebagai gantinya, gunakan UrlRequest.read() untuk membaca bagian isi berikutnya ke dalam buffering yang Anda berikan. Setelah selesai membaca potongan isi respons, Cronet akan memanggil metode onReadCompleted. Proses ini berulang sampai tidak ada data lagi untuk dibaca.

39d71a5e85f151d8.png

Mari kita mulai mengimplementasikan siklus baca. Pertama, buat instance aliran output array byte baru dan saluran yang menggunakannya. Kita akan menggunakan saluran tersebut sebagai sink untuk isi respons.

private val bytesReceived = ByteArrayOutputStream()
private val receiveChannel = Channels.newChannel(bytesReceived)

Berikutnya, implementasikan metode onReadCompleted untuk menyalin data dari buffering byte ke sink dan memanggil pembacaan berikutnya.

// The byte buffer we're getting in the callback hasn't been flipped for reading,
// so flip it so we can read the content.
byteBuffer.flip()
receiveChannel.write(byteBuffer)

// Reset the buffer to prepare it for the next read
byteBuffer.clear()

// Continue reading the request
request.read(byteBuffer)

Untuk menyelesaikan loop pembacaan isi, panggil operasi baca awal dari metode callback onResponseStarted. Perhatikan bahwa Anda perlu menggunakan buffering byte langsung dengan Cronet. Meskipun kapasitas buffering tidak penting untuk tujuan codelab, 16 KiB adalah nilai default yang baik untuk sebagian besar penggunaan produksi.

request.read(ByteBuffer.allocateDirect(BYTE_BUFFER_CAPACITY_BYTES))

Mari selesaikan class lainnya sekarang. Pengalihan tidak terlalu menarik bagi Anda, jadi cukup ikuti pengalihan seperti yang dilakukan browser web Anda.

override fun onRedirectReceived(
   request: UrlRequest, info: UrlResponseInfo?, newLocationUrl: String?
) {
   request.followRedirect()
}

Terakhir, kita perlu menangani metode onSucceeded dan onFailed. onFailed cocok dengan tanda tangan yang ingin Anda berikan untuk pengguna callback helper sehingga Anda dapat menghapus definisi dan mengizinkan perluasan class menggantikan metode tersebut. onSucceeded harus meneruskan downstream isi sebagai array byte. Tambahkan metode abstrak baru dengan isi tanda tangannya.

abstract fun onSucceeded(
   request: UrlRequest, info: UrlResponseInfo, bodyBytes: ByteArray)

Kemudian, pastikan metode onSucceeded yang baru dipanggil dengan benar saat permintaan berhasil diselesaikan.

final override fun onSucceeded(request: UrlRequest, info: UrlResponseInfo) {
   val bodyBytes = bytesReceived.toByteArray()
   onSucceeded(request, info, bodyBytes)
}

Lihat, Anda telah mempelajari cara menerapkan callback Cronet!

9. Mengimplementasikan downloader gambar

Mari kita gunakan callback yang kita buat di bagian sebelumnya untuk mengimplementasikan downloader gambar berbasis Cronet.

Buat class baru bernama CronetImageDownloader yang mengimplementasikan antarmuka ImageDownloader dan menerima CronetEngine sebagai parameter konstruktornya.

class CronetImageDownloader(val engine: CronetEngine) : ImageDownloader {
   override suspend fun downloadImage(url: String): ImageDownloaderResult {
       TODO("Not yet implemented")
   }
}

Untuk mengimplementasikan metode downloadImage, Anda perlu mempelajari cara membuat permintaan Cronet. Caranya sangat mudah - panggil metode newUrlRequestBuilder() dari CronetEngine Anda. Metode ini mengambil URL, instance class callback, dan eksekutor yang menjalankan metode callback Anda.

val request = engine.newUrlRequestBuilder(url, callback, executor)

URL ini kita ketahui dari parameter downloadImage. Untuk eksekutor, kita akan membuat kolom seluruh instance.

private val executor = Executors.newSingleThreadExecutor()

Terakhir, kita menggunakan implementasi callback helper dari bagian sebelumnya untuk mengimplementasikan callback. Kita tidak akan membahas detail implementasinya karena ini merupakan topik coroutine Kotlin. Anda dapat menganggap cont.resume sebagai return dari metode downloadImage.

Jika disatukan, implementasi downloadImage Anda akan menyerupai cuplikan berikut.

override suspend fun downloadImage(url: String): ImageDownloaderResult {
   val startNanoTime = System.nanoTime()
   return suspendCoroutine {
       cont ->
       val request = engine.newUrlRequestBuilder(url, object: ReadToMemoryCronetCallback() {
       override fun onSucceeded(
           request: UrlRequest,
           info: UrlResponseInfo,
           bodyBytes: ByteArray) {
           cont.resume(ImageDownloaderResult(
               successful = true,
               blob = bodyBytes,
               latency = Duration.ofNanos(System.nanoTime() - startNanoTime),
               wasCached = info.wasCached(),
               downloaderRef = this@CronetImageDownloader))
       }

       override fun onFailed(
           request: UrlRequest,
           info: UrlResponseInfo,
           error: CronetException
       ) {
           Log.w(LOGGER_TAG, "Cronet download failed!", error)
           cont.resume(ImageDownloaderResult(
               successful = false,
               blob = ByteArray(0),
               latency = Duration.ZERO,
               wasCached = info.wasCached(),
               downloaderRef = this@CronetImageDownloader))
       }
   }, executor)
       request.build().start()
   }
}

10. Penggabungan akhir

Mari kembali ke composable MainDisplay dan tangani TODO terakhir menggunakan downloader gambar yang baru saja kita buat.

imageDownloader = CronetImageDownloader(cronetEngine)

Dan, selesai! Coba jalankan aplikasi. Anda akan melihat permintaan Anda dirutekan melalui downloader gambar Cronet.

11. Penyesuaian

Anda dapat menyesuaikan perilaku permintaan di tingkat permintaan dan tingkat mesin. Kita akan mendemonstrasikan hal ini dengan cache, tetapi ada banyak opsi lainnya. Untuk mengetahui detailnya, lihat dokumentasi UrlRequest.Builder dan CronetEngine.Builder.

Untuk mengaktifkan penyimpanan cache pada tingkat mesin, gunakan metode enableHttpCache builder. Pada contoh di bawah ini, kita menggunakan cache dalam memori. Untuk opsi lain yang tersedia, lihat dokumentasi. Selanjutnya, mesin Cronet akan menjadi:

val cronetEngine = CronetEngine.Builder(ctx)
   .enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 10 * 1024 * 1024)
   .build()

Jalankan aplikasi dan tambahkan beberapa gambar. Gambar yang ditambahkan berulang kali harus memiliki latensi yang jauh lebih pendek dan UI harus menunjukkan bahwa gambar di-cache.

Fungsi ini dapat diganti per permintaan. Mari kita masukkan peretasan kecil di downloader Cronet dan nonaktifkan penyimpanan cache untuk gambar Matahari, yang merupakan gambar pertama dari daftar URL.

if (url == CronetCodelabConstants.URLS[0]) {
   request.disableCache()
}

request.build().start()

Sekarang jalankan aplikasi lagi. Anda akan melihat bahwa gambar matahari tidak di-cache.

d9d0163c96049081.png

12. Kesimpulan

Selamat, Anda telah mencapai akhir codelab. Selama prosesnya, Anda telah mempelajari dasar-dasar cara menggunakan Cronet.

Untuk mempelajari Cronet lebih lanjut, lihat panduan developer dan kode sumber. Selain itu, Anda dapat berlangganan blog Developer Android untuk menjadi yang pertama menerima kabar terbaru tentang Cronet & berita Android umum.