复制enqueue()方法

时间:2019-11-13 19:34:21

标签: kotlin okhttp

我有以下方法,可以简单地以太SYNC或ASYNC方式获取数据:

enum class CallType { SYNC, ASYNC }

suspend fun get( url: String, callType : CallType, mock : String? = null, callback: Callback? ): Response?
{
    var response : okhttp3.Response ?= null

    val request = Request.Builder()
        .url( url )
        .build()

    val call = client.newCall( request )

    if( mock != null )
    {
        // this works fine for SYNC, but how to make it work with ASYNC callback?
        delay( 1000 )
        return okhttp3.Response.Builder().body(
            ResponseBody.create( "application/json".toMediaType(), mock )
        ).build()
    }

    if( callType == CallType.ASYNC && callback != null )
        call.enqueue( callback )
    else
        response = call.execute()

    return response
}

我希望能够模拟/覆盖响应。在执行SYNC方式时,我可以做到这一点,因为我只需要构造并返回一个伪造的okhttp3.response(如下面的代码片段),并且代码执行就会停止并且一切都很好:

    if( mock != null )
    {
        delay( 1000 )
        return okhttp3.Response.Builder().body(
            ResponseBody.create( "application/json".toMediaType(), mock )
        ).build()
    }

问题是我希望能够对ASYNC调用执行相同的操作,但是我不确定从这里开始该去哪里。我基本上是在尝试复制enqueue()方法,以便在一段时间延迟后触发我的回调(已传递给get()方法)和我的假okhttp3.Response是通过回调返回的,而不是 return 。关于如何做到这一点的任何建议?谢谢!

2 个答案:

答案 0 :(得分:1)

一种简单的方法是仅以同步方式调用回调:

if (mock != null) {
  val response = ... // prepare mock response here
  callback.onResponse(response)
}

因此,即使在您的get函数完成之前,回调也将被调用。

如果要实现响应实际上是异步传递,则需要从额外的协程执行模拟传递

if (mock != null) {
   GlobalScope.launch {
      val response = ... // prepare mock response here
      delay(1000)
      callback.onResponse(response)
   }
}

答案 1 :(得分:1)

您正在将不同的概念与实现结合在一起。异步应该使用CoroutineContext而不是参数来控制。这样,您将始终返回非null值。隐藏实现细节(此处为 OkHttp )并且不公开它也是明智的选择。

您可以使用suspendCoroutine来将 OkHttp 与同伴小程序桥接起来。

suspend fun get(
    url: String,
    mock : String? = null
) = if(mock != null) {
    delay( 1000 )
    Response.Builder().body(
        ResponseBody.create(
            "application/json".toMediaType()
            mock
        )
    ).build()
} else suspendCoroutine { continuation ->
    client.newCall(
        Request.Builder()
            .url(url)
            .build()
    ).enqueue(
        object : Callback {
            override fun onFailure(call: Call, e: IOException) =
                continuation.resumeWithException(e)
            override fun onResponse(call: Call, response: Response) =
                continuation.resume(response)
        }
    )
}

要同步访问,只需使用

runBlocking { get(url, mock) }

如果您确实需要提供自己的Callable,则可以轻松地委派给它。但是,即使模拟响应时您不需要它,您也必须创建一个呼叫。