Comment j'ai fait un disjoncteur Okhttp personnalisé à travers les coroutines de Kotlin

Commençons par l'énoncé du problÚme.

  1. Il est nĂ©cessaire d'envoyer le jeton et l'ID utilisateur dans chaque demande dans l'en-tĂȘte
  2. Il est nĂ©cessaire de tirer de chaque rĂ©ponse un nouveau jeton et un nouvel identifiant d'utilisateur Ă  partir des en-tĂȘtes
  3. Les donnĂ©es reçues doivent ĂȘtre enregistrĂ©es.

La bibliothĂšque pour l'interaction avec le serveur est Retrofit. Les coroutines sont responsables du multithreading.
La tĂąche n'est pas difficile, il vous suffit d'ajouter le disjoncteur client Okhttp Ă  chaque requĂȘte. Une demi-heure et tout est prĂȘt, tout fonctionne, tout le monde est content. Mais je me demandais, est-il possible de faire un disjoncteur sans client Okhttp?

Commençons par rĂ©soudre les problĂšmes dans l'ordre. S'il n'y a pas de problĂšme avec l'ajout d'en-tĂȘte (il vous suffit d'ajouter @HeaderMap dans la demande), alors comment obtenir les en-tĂȘtes qui viennent dans la rĂ©ponse? TrĂšs simple, nous devons encapsuler notre rĂ©ponse dans la classe Response, qui a une mĂ©thode headers ().

Voici l'interface de requĂȘte:

@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 

Mais cela est devenu:

 @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> 

Maintenant, pour chaque demande, vous devez ajouter le paramĂštre headersMap. CrĂ©ons une classe RestClient distincte pour le shell de requĂȘte afin que le jeton et l'ID ne soient pas extraits constamment de sharedPreferences dans le prĂ©sentateur. Voici comment cela se rĂ©vĂšle:

 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() ) } } 

On voit que nous faisons la mĂȘme chose:

  1. Nous obtenons quelques paramĂštres pour la demande.
  2. Ajoutez des en-tĂȘtes Ă  la demande.
  3. Appelez la méthode.
  4. Nous obtenons de nouvelles valeurs des en-tĂȘtes.
  5. Nous retournons le résultat.

Pourquoi ne faisons-nous pas une seule fonction pour toutes les demandes? Pour ce faire, modifiez les requĂȘtes. Au lieu de variables individuelles de type @Field, nous allons maintenant utiliser @FieldMap. Ce sera le premier paramĂštre de notre fonction - le percher. Le deuxiĂšme paramĂštre que nous aurons est la requĂȘte elle-mĂȘme. Ici, j'ai utilisĂ© Kotlin DSL (je voulais vraiment). J'ai créé la classe Request dans laquelle j'ai créé la fonction send pour appeler la requĂȘte.

Voici Ă  quoi ressemble l'interface de requĂȘte:

 @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> 

Et voici la classe Request:

 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 ?: " ") } } } } 

Maintenant, la classe RestClient ressemble Ă  ceci:

 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) } 

Et enfin, voici comment nous appelons nos demandes dans le présentateur:

 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() } 

Ici, j'ai fait un disjoncteur personnalisé avec l'aide de Kotlin.

PS La solution à ce problÚme était trÚs excitante, mais, malheureusement, le projet utilise un disjoncteur Okhttp.

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


All Articles