Semakin saya membaca dan menonton laporan coroutine di Kotlin, semakin saya mengagumi alat bahasa ini. Rilis stabil mereka baru-baru ini dirilis di Kotlin 1.3, yang berarti sudah saatnya untuk memulai penyelaman dan mencoba coroutine dalam aksi menggunakan kode RxJava saya yang ada sebagai contoh. Dalam posting ini, kita akan fokus pada bagaimana mengambil permintaan jaringan yang ada dan mengonversinya dengan mengganti RxJava dengan coroutine.

Terus terang, sebelum saya mencoba coroutine, saya pikir mereka sangat berbeda dari sebelumnya. Namun, prinsip dasar corutin mencakup konsep yang sama yang kita gunakan dalam aliran reaktif RxJava. Sebagai contoh, mari kita ambil konfigurasi RxJava sederhana untuk membuat permintaan jaringan dari salah satu aplikasi saya:
- Kami mendefinisikan antarmuka jaringan untuk Retrofit menggunakan adaptor Rx ( retrofit2: adaptor-rxjava2 ). Fungsi akan mengembalikan objek dari framework Rx, seperti Single atau Observable . (Fungsi digunakan di sini dan bukan metode, karena diasumsikan bahwa kode lama juga ditulis di Kotlin. Nah, atau dikonversi dari Jawa melalui Android Studio).
- Kami memanggil fungsi tertentu dari kelas lain (misalnya, repositori, atau aktivitas).
- Kami menentukan utas untuk Penjadwal mana mereka akan dieksekusi dan mengembalikan hasilnya (metode .subscribeOn () dan .observeOn () ).
- Kami menyimpan tautan ke objek untuk berhenti berlangganan (misalnya, di CompositeObservable).
- Berlangganan aliran acara.
- Berhenti berlangganan dari aliran tergantung pada peristiwa dari siklus hidup Aktivitas.
Ini adalah algoritma dasar untuk bekerja dengan Rx (tidak memperhitungkan fungsi pemetaan dan rincian manipulasi data lainnya). Sedangkan untuk corutin, prinsipnya tidak banyak berubah. Konsep yang sama, hanya terminologi yang berubah.
- Kami mendefinisikan antarmuka jaringan untuk Retrofit menggunakan adaptor untuk coroutine . Fungsi akan mengembalikan objek yang Ditangguhkan dari API Corutin.
- Kami memanggil fungsi-fungsi ini dari kelas lain (misalnya, repositori, atau aktivitas). Satu-satunya perbedaan: setiap fungsi harus ditandai sebagai ditangguhkan .
- Tentukan dispatcher yang akan digunakan untuk coroutine.
- Kami menyimpan tautan ke objek Pekerjaan untuk berhenti berlangganan.
- Jalankan coroutine dengan cara apa pun yang memungkinkan.
- Kami membatalkan coroutine tergantung pada peristiwa dari siklus hidup Kegiatan.
Seperti yang Anda lihat dari urutan di atas, proses eksekusi Rx dan Corutin sangat mirip. Jika kami tidak memperhitungkan detail implementasi, ini berarti kami dapat mempertahankan pendekatan yang kami miliki - kami hanya mengganti beberapa hal untuk menjadikan implementasi kami ramah-coroutine.

Langkah pertama yang harus kita ambil adalah mengizinkan Retrofit mengembalikan objek yang ditangguhkan . Objek yang ditangguhkan adalah berjangka non-pemblokiran yang dapat dibatalkan jika perlu. Objek-objek ini pada dasarnya adalah Pekerjaan coroutine, yang berisi nilai untuk pekerjaan yang sesuai. Menggunakan tipe Deferred memungkinkan kita untuk mencampurkan ide yang sama dengan Job, dengan tambahan kemampuan untuk mendapatkan status tambahan, seperti keberhasilan atau kegagalan - yang membuatnya ideal untuk permintaan jaringan.
Jika Anda menggunakan Retrofit dengan RxJava, Anda mungkin menggunakan Pabrik Adaptor Panggilan RxJava. Untungnya, Jake Worton menulis padanannya untuk coroutine .
Kita dapat menggunakan adaptor panggilan ini di pembuat Retrofit, dan kemudian mengimplementasikan antarmuka Retrofit kami dengan cara yang sama seperti dengan RxJava:
private fun makeService(okHttpClient: OkHttpClient): MyService { val retrofit = Retrofit.Builder() .baseUrl("some_api") .client(okHttpClient) .addCallAdapterFactory(CoroutineCallAdapterFactory()) .build() return retrofit.create(MyService::class.java) }
Sekarang mari kita lihat antarmuka MyService, yang digunakan di atas. Kita harus mengganti tipe yang dapat diobservasi yang dikembalikan dengan Ditangguhkan di antarmuka Retrofit. Jika dulu seperti ini:
@GET("some_endpoint") fun getData(): Observable<List<MyData>>
Sekarang kita ganti dengan:
@GET("some_endpoint") fun getData(): Deferred<List<MyData>>
Setiap kali kita memanggil getData (), objek Ditangguhkan akan kembali kepada kita - analog dari permintaan Ayub untuk jaringan. Sebelumnya, kami entah bagaimana menyebut fungsi ini dengan RxJava:
override fun getData(): Observable<List<MyData>> { return myService.getData() .map { result -> result.map { myDataMapper.mapFromRemote(it) } } }
Dalam aliran RxJava ini, kami memanggil fungsi utilitas kami, kemudian menerapkan operasi peta dari API RxJava dengan pemetaan data yang dikembalikan dari permintaan ke sesuatu yang digunakan di lapisan UI. Ini akan sedikit berubah ketika kita menggunakan implementasi dengan coroutine. Sebagai permulaan, fungsi kita harus ditunda (ditangguhkan), untuk melakukan operasi yang malas di dalam tubuh fungsi. Dan untuk ini, fungsi panggilan juga harus ditangguhkan. Fungsi yang ditangguhkan adalah non-blocking, dan dapat dikontrol setelah awalnya disebut. Anda dapat memulainya, menjeda, melanjutkan atau membatalkannya.
override suspend fun getData(): List<MyData> { ... }
Sekarang kita harus memanggil fungsi utilitas kita. Pada pandangan pertama, kita melakukan hal yang sama, tetapi kita harus ingat bahwa sekarang kita ditangguhkan alih-alih diamati .
override suspend fun getData(): List<MyData> { val result = myService.getData() ... }
Karena perubahan ini, kami tidak dapat lagi menggunakan rantai operasi peta dari RxJava API. Dan bahkan pada titik ini, data tidak tersedia bagi kami - kami hanya memiliki instance Deferred. Sekarang kita harus menggunakan fungsi await () untuk menunggu hasil dari query dan kemudian melanjutkan untuk mengeksekusi kode di dalam fungsi:
override suspend fun getData(): List<MyData> { val result = myService.getData().await() ... }
Pada titik ini, kami mendapatkan permintaan yang lengkap dan data darinya tersedia untuk digunakan. Karena itu, kami sekarang dapat melakukan operasi pemetaan:
override suspend fun getData(): List<MyData> { val result = myService.getData().await() return result.map { myDataMapper.mapFromRemote(it) } }
Kami mengambil antarmuka Retrofit kami bersama dengan kelas panggilan dan menggunakan coroutine. Sekarang kami ingin memanggil kode ini dari Aktivitas atau fragmen kami dan menggunakan data yang kami dapatkan dari jaringan.
Dalam Aktivitas kami, kami akan mulai dengan membuat tautan ke Ayub, di mana kami dapat menetapkan operasi coroutine kami dan kemudian menggunakannya untuk mengontrol, misalnya, membatalkan permintaan, selama panggilan onDestroy () .
private var myJob: Job? = null override fun onDestroy() { myJob?.cancel() super.onDestroy() }
Sekarang kita dapat menetapkan sesuatu ke variabel myJob. Mari kita lihat permintaan kami dengan coroutine:
myJob = CoroutineScope(Dispatchers.IO).launch { val result = repo.getLeagues() withContext(Dispatchers.Main) { //do something with result } }
Dalam posting ini, saya tidak ingin menyelidiki Dispatcher atau melakukan operasi dalam coroutine, karena ini adalah topik untuk posting lain. Singkatnya, apa yang terjadi di sini:
- Buat instance CoroutineScope menggunakan IO Dispatcher sebagai parameter. Dispatcher ini digunakan untuk melakukan pemblokiran operasi I / O, seperti permintaan jaringan.
- Kami meluncurkan coroutine kami dengan fungsi peluncuran - fungsi ini meluncurkan coroutine baru dan mengembalikan tautan ke variabel tipe Job.
- Kemudian kami menggunakan tautan ke repositori kami untuk menerima data dengan melakukan permintaan jaringan.
- Pada akhirnya, kami menggunakan operator utama untuk melakukan pekerjaan pada utas UI. Di sini kita dapat menampilkan data yang diterima kepada pengguna.
Dalam posting berikutnya, penulis berjanji untuk menggali sedikit lebih dalam ke detail, tetapi bahan saat ini harus cukup untuk mulai mempelajari coroutine.
Dalam posting ini, kami mengganti implementasi RxJava dari respons Retrofit dengan objek Ditangguhkan dari API Corutin. Kami memanggil fungsi-fungsi ini untuk menerima data dari jaringan, dan kemudian menampilkannya dalam aktivitas kami. Saya harap Anda melihat betapa sedikit perubahan yang perlu Anda lakukan untuk memulai dengan coroutine, dan menghargai kesederhanaan API, terutama ketika membaca dan menulis kode.
Dalam komentar di posting asli, saya menemukan permintaan tradisional: tampilkan seluruh kode. Oleh karena itu, saya membuat aplikasi sederhana yang, pada saat startup, mendapatkan jadwal kereta api dengan Yandex. Schedule API dan menampilkannya di RecyclerView. Tautan: https://github.com/AndreySBer/RetrofitCoroutinesExample
Saya juga ingin menambahkan bahwa coroutine tampaknya menjadi pengganti yang lebih rendah untuk RxJava, karena mereka tidak menawarkan serangkaian operasi yang setara untuk menyinkronkan utas. Dalam hal ini, perlu melihat implementasi ReactiveX untuk Kotlin: RxKotlin .
Jika Anda menggunakan Android Jetpack, saya juga menemukan contoh dengan Retrofit, coroutine, LiveData, dan MVVM: https://codinginfinite.com/kotlin-coroutine-call-adapter-retrofit/