Bagaimana saya membuat pemecah Okhttp khusus melalui coroutine Kotlin

Mari kita mulai dengan pernyataan masalahnya.

  1. Anda perlu mengirim token dan id pengguna di setiap permintaan di header
  2. Penting untuk menarik dari setiap jawaban token baru dan id pengguna dari header
  3. Data yang diterima harus disimpan.

Perpustakaan untuk interaksi server adalah Retrofit. Coroutine bertanggung jawab atas multithreading.
Tugasnya tidak sulit, Anda hanya perlu menambahkan pemutus klien Okhttp untuk setiap permintaan. Setengah jam dan semuanya sudah siap, semuanya berfungsi, semua orang senang. Tapi saya bertanya-tanya, apakah mungkin membuat pemutus tanpa klien Okhttp?

Mari kita mulai memecahkan masalah secara berurutan. Jika tidak ada masalah dengan menambahkan header (Anda hanya perlu menambahkan @HeaderMap dalam permintaan), lalu bagaimana cara mendapatkan header yang datang dalam respons? Sangat sederhana, kita perlu membungkus respons kita di kelas Respons, yang memiliki metode header ().

Inilah antarmuka kueri:

@FormUrlEncoded @POST("someurl/") suspend fun request1(@Field("idLast") idLastFeed: Long, @Field("autoview") autoView: Boolean, @HeaderMap headers: Map<String, String?>): Answer1 @FormUrlEncoded @POST("someurl/") suspend fun request2(@Field("ransom") ransom: Long, @HeaderMap headers: Map<String, String?>): Answer2 

Tetapi ini telah menjadi:

 @FormUrlEncoded @POST("someurl") suspend fun request1(@Field("idLast") idLastFeed: Long, @Field("autoview") autoView: Boolean, @HeaderMap headers: Map<String, String?>?): Response<Answer1> @FormUrlEncoded @POST("someurl") suspend fun request2(@Field("ransom") ransom: Long, @HeaderMap headers: Map<String, String?>?): Response<Answer2> 

Sekarang untuk setiap permintaan Anda perlu menambahkan parameter headersMap. Mari kita buat kelas RestClient yang terpisah untuk shell kueri agar token dan id tidak ditarik keluar dari sharedPreferences secara konstan di presenter. Begini hasilnya:

 class RestClient(private val api: Api, private val prefs: SharedPreferences) { suspend fun request1(last: Long, autoView: Boolean): Answer1 { return api.request1(last, autoView, headers()) } suspend fun request2(id: Long): Answer2 { return api.request2(id, headers()) } private val TOKEN_KEY = "Token" private val ID_KEY = "ID" fun headers(): Map<String, String> { return mapOf( TOKEN_KEY to prefs.getString(Constants.Preferences.SP_TOKEN_KEY, ""), ID_KEY to prefs.getLong(Constants.Preferences.SP_ID, -1).toString() ) } } 

Dapat dilihat bahwa kami melakukan hal yang sama:

  1. Kami mendapatkan beberapa parameter untuk permintaan tersebut.
  2. Tambahkan header ke permintaan.
  3. Panggil metode.
  4. Kami mendapatkan nilai baru dari tajuk.
  5. Kami mengembalikan hasilnya.

Mengapa kita tidak membuat satu fungsi untuk semua permintaan? Untuk melakukan ini, ubah kueri. Alih-alih variabel individual dari tipe @ Field, kami sekarang akan menggunakan @ Field. Ini akan menjadi parameter pertama untuk fungsi kami - yang bertengger. Parameter kedua yang akan kita miliki adalah permintaan itu sendiri. Di sini saya menggunakan Kotlin DSL (saya benar-benar ingin). Saya membuat kelas Permintaan di mana saya membuat fungsi kirim untuk memanggil permintaan.

Seperti inilah tampilan antarmuka kueri:

 @FormUrlEncoded @POST("someurl/") suspend fun feedListMap(@FieldMap map: HashMap<String, out Any>?, @HeaderMap headers: Map<String, String?>?): Response<Answer1> @FormUrlEncoded @POST("someurl/") suspend fun feedListMap(@FieldMap map: HashMap<String, out Any>?, @HeaderMap headers: Map<String, String?>?): Response<Answer2> 

Dan di sini adalah kelas Permintaan:

 class Request<T>( var fieldHashMap: java.util.HashMap<String, out Any> = hashMapOf(), var headersHashMap: Map<String, String?>? = mapOf(), var req: suspend (HashMap<String, out Any>?, Map<String, String?>?) -> Response<T>? = { _,_ -> null} ){ fun send(): Response<T>? { return runBlocking { try { req.invoke(fieldHashMap, headersHashMap) } catch (e: Exception) { throw Exception(e.message ?: " ") } catch (t: Throwable) { throw Exception(t.message ?: " ") } } } } 

Sekarang kelas RestClient terlihat seperti ini:

 class RestClient(private val api: Api, private val prefs: SharedPreferences) { private val TOKEN_KEY = "Token" private val ID_KEY = "ID" fun headers(): Map<String, String> { return mapOf( TOKEN_KEY to prefs.getString(Constants.Preferences.SP_TOKEN_KEY, ""), ID_KEY to prefs.getLong(Constants.Preferences.SP_ID, -1).toString() ) } fun <T> buildRequest(request: Request<T>.() -> Unit): T? { val req = Request<T>() request(req) val res = req.send() val newToken = res?.headers()?.get(TOKEN_KEY) val newID = res?.headers()?.get(ID_KEY)?.toLong() if (newToken.notNull() && newID.notNull()) { prefs.edit() .putString(TOKEN_KEY, newToken) .putLong(ID_KEY, newID) .apply() } return res?.body() } fun fiedsMapForRequest1(last: Long, autoView: Boolean) = hashMapOf("idLast" to last, "autoview" to autoView) fun fiedsMapForRequest2(ransom: Long, autoView: Boolean) = hashMapOf("ransom" to ransom) } 

Dan akhirnya, ini adalah bagaimana kami memanggil permintaan kami di presenter:

 try { val answer1 = restClient.buildRequest<Answer1> { fieldHashMap = restClient.fiedsMapForRequest1(1, false) headersHashMap = restClient.headers() req = api::request1 } val answer2 = restClient.buildRequest<Answer2> { fieldHashMap = restClient.fiedsMapForRequest2(1234) headersHashMap = restClient.headers() req = api::request2 } // do something with answer } catch (e: Exception) { viewState.showError(e.message.toString()) } finally { viewState.hideProgress() } 

Di sini saya membuat pemecah kebiasaan dengan bantuan Kotlin.

PS Solusi untuk masalah ini sangat menarik, tetapi, sayangnya, proyek ini menggunakan pemutus Okhttp.

Source: https://habr.com/ru/post/id465781/


All Articles