我如何通过Kotlin的协程制作定制的Okhttp断路器

让我们从问题的陈述开始。

  1. 必须在标头的每个请求中发送令牌和用户ID
  2. 有必要从每个答案中提取新的令牌和标头中的用户ID
  3. 接收的数据必须保存。

服务器交互的库是Retrofit。 协程负责多线程。
这项任务并不困难,您只需要向每个请求添加Okhttp客户中断程序即可。 半小时,一切准备就绪,一切正常,每个人都很高兴。 但是我想知道,没有Okhttp客户端是否有可能制造断路器?

让我们开始按顺序解决问题。 如果添加标题没有问题(您只需要在请求中添加@HeaderMap),那么如何获取响应中出现的标题? 非常简单,我们需要将响应包装在Response类中,该类具有headers()方法。

这是查询界面:

@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 

但这变成了:

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

现在,对于每个请求,您需要添加headersMap参数。 让我们为查询外壳程序创建一个单独的RestClient类,以便在演示者中不会将令牌和ID不断地从sharedPreferences中拉出。 结果是这样的:

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

可以看出我们正在做同样的事情:

  1. 我们为请求获取了一些参数。
  2. 将标头添加到请求。
  3. 调用方法。
  4. 我们从标题获取新值。
  5. 我们返回结果。

我们为什么不对所有请求都执行一个功能? 为此,修改查询。 现在,我们将使用@FieldMap代替@Field类型的单个变量。 这将是我们函数的第一个参数-percher。 我们将拥有的第二个参数是请求本身。 在这里,我使用了Kotlin DSL(我真的很想这样做)。 我创建了Request类,在其中创建了send函数来调用请求。

查询界面如下所示:

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

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

现在,RestClient类如下所示:

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

最后,这是我们在演示者中调用我们的请求的方式:

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

在这里,我借助Kotlin制作了一个定制断路器。

PS解决此问题的方法非常令人兴奋,但不幸的是,该项目使用Okhttp断路器。

Source: https://habr.com/ru/post/zh-CN465781/


All Articles