我正在创建 http json 客户端。我将 Volley 与协程结合使用。我想创建通用的 http 客户端,这样我就可以在任何地方使用它。
我创建了通用扩展方法来将 JSON 字符串解析为对象。
inline fun <reified T>String.jsonToObject(exclusionStrategy: ExclusionStrategy? = null) : T {
val builder = GsonBuilder()
if(exclusionStrategy != null){
builder.setExclusionStrategies(exclusionStrategy)
}
return builder.create().fromJson(this, object: TypeToken<T>() {}.type)
}
问题是,当我调用这个方法时,我没有得到预期的结果。第一次调用给出了正确的结果。对象已初始化。但是第二次调用,我使用传递给方法的通用参数,以异常“LinkedTreeMap 无法转换为令牌”结束。
protected inline fun <reified T>sendRequestAsync(endpoint: String, data: Any?, method: Int, token: Token?): Deferred<T> {
return ioScope.async {
suspendCoroutine<T> { continuation ->
val jsonObjectRequest = HttpClient.createJsonObjectRequest(
endpoint,
data?.toJsonString(),
method,
Response.Listener {
//this call is successful and object is initialized
val parsedObject : HttpResponse<Token> = it.toString().jsonToObject()
//this call is not successful and object is not initialized properly
val brokenObject : HttpResponse<T> = it.toString().jsonToObject()
continuation.resume(brokenObject.response)
},
Response.ErrorListener {
continuation.resumeWithException(parseException(it))
},
token)
HttpClient.getInstance(context).addToRequestQueue(jsonObjectRequest)
}
}
}
泛型方法的调用。
fun loginAsync(loginData: LoginData): Deferred<Token> {
return sendRequestAsync("/tokens/", loginData, Request.Method.POST, null)
}
这就是 httpresponse 数据类的样子。
data class HttpResponse<T> (
val response: T
)
我在这里看到了使用 Type::class.java 的解决方法,但我不喜欢这种方法,我想使用具体化和内联关键字。Kotlin 中的 reified 关键字如何工作? https://stackoverflow.com/questions/45949584/how-does-the-reified-keyword-in-kotlin-work
UPDATE这是我得到的异常。
java.lang.ClassCastException:com.google.gson.internal.LinkedTreeMap 无法转换为 com.xbionicsphere.x_card.entities.Token
可能的解决方法我找到了可能的解决方法。如果我创建将从响应中解析令牌的方法,并在executeRequestAsync中使用此方法,一切都会开始工作,但我不喜欢这个解决方案,因为我必须为每个请求添加额外的参数。
新登录异步
fun loginAsync(loginData: LoginData): Deferred<Token> {
val convertToResponse : (JSONObject) -> HttpResponse<Token> = {
it.toString().jsonToObject()
}
return executeRequestAsync("/tokens/", loginData, Request.Method.POST, null, convertToResponse)
}
新的executeRequestAsync
protected inline fun <reified T>executeRequestAsync(endpoint: String, data: Any?, method: Int, token: Token?, crossinline responseProvider: (JSONObject) -> HttpResponse<T>): Deferred<T> {
return ioScope.async {
suspendCoroutine<T> { continuation ->
val jsonObjectRequest =
HttpClient.createJsonObjectRequest(
endpoint,
data?.toJsonString(),
method,
Response.Listener {
val response: HttpResponse<T> = responseProvider(it)
continuation.resume(response.response)
},
Response.ErrorListener {
continuation.resumeWithException(parseException(it))
},
token
)
HttpClient.getInstance(
context
).addToRequestQueue(jsonObjectRequest)
}
}
}
UPDATE我可能已经找到了可行的解决方案。 executeRequestAsync 需要通过泛型参数提供最终类型定义,因此我增强了方法的声明。现在方法声明如下所示:
protected inline fun <reified HttpResponseOfType, Type>executeRequestAsync(endpoint: String, data: Any?, method: Int, token: Token?) : Deferred<Type> where HttpResponseOfType : HttpResponse<Type> {
val scopedContext = context
return ioScope.async {
suspendCoroutine<Type> { continuation ->
val jsonObjectRequest =
HttpClient.createJsonObjectRequest(
endpoint,
data?.toJsonString(),
method,
Response.Listener {
val response: HttpResponseOfType = it.toString().jsonToObject()
continuation.resume(response.response)
},
Response.ErrorListener {
continuation.resumeWithException(parseException(it))
},
token
)
HttpClient.getInstance(
scopedContext
).addToRequestQueue(jsonObjectRequest)
}
}
}
感谢这个复杂的函数声明,我可以通过此调用执行请求:
fun loginAsync(loginData: LoginData): Deferred<Token> {
return executeRequestAsync("/tokens/", loginData, Request.Method.POST, null)
}