【问题标题】:How replace callback by Kotlin's coroutines如何用 Kotlin 的协程替换回调
【发布时间】:2019-12-06 19:24:52
【问题描述】:

我需要做下一个:

  1. 检查是否存在配置文件
  2. 如果存在,则通过 Retrofit lib 启动异步 http 请求 (getAdvertising())
  3. 如果不存在,则首先从远程加载配置文件(通过 Retrofit),如果成功获取结果,则启动异步 http 请求 (getAdvertising())

我通过这样的回调来做到这一点:

    fun getAdvertising(callback: Callback<List<Advertising>>) {
        val call = myRestClient.advertising
        executeAsync(call, callback)
    }

  private fun <T> executeAsync(call: Call<T>, callback: Callback<T>) {
        val currentApplicationProfileResponse = foService.applicationProfileResponse
        if (currentApplicationProfileResponse == null) {
            getApplicationProfile(object : DefaultRestClientCallback<ApplicationProfileResponse>() {

                override fun onTransportResponse(transportResponse: TransportResponse) {
                    super.onTransportResponse(transportResponse)
                    if (transportResponse.isSuccessful) {
                        //asynchronously
                        call.enqueue(callback)
                    } else { // not success
                        if (callback is DefaultRestClientCallback<*>) {
                            callback.onTransportResponse(transportResponse)
                        } else {
                            callback.onResponse(call, transportResponse.originalResponse as Response<T>?)
                        }
                    }
                }
            })
        } else { // appProfile is not null
            //asynchronously
            call.enqueue(callback)
        }
    }

很好,工作正常。

但是代码太多了。是否可以用 Kotlin 的协程替换 Callback?

【问题讨论】:

标签: android retrofit2 kotlin-coroutines


【解决方案1】:
private fun getADvertise(onResult: (ArrayList<String>) -> Unit = {}) {
    CoroutineScope(Dispatchers.IO).launch {
        //Do Request To get Data using retrofit
        val result = ArrayList<String>()//result from retrofit
        withContext(Dispatchers.Main)
        {
            onResult(result)
        }

    }
}

private fun isProfileExist(): Boolean {
    //true or false
    return true
}

private fun getProfilePicture(id: String, OnResult: (String) -> Unit = {}) {
    CoroutineScope(Dispatchers.IO).launch {
        //Do Request To get Data using retrofit
        val result = "Link"//result from retrofit
        withContext(Dispatchers.Main)
        {
            OnResult(result)
        }

    }
}

//---------your main function------------------>
public fun execute(onResultMain: (ArrayList<String>) -> Unit = {}) {
    val exist = isProfileExist()
    if (exist) {
        getADvertise(onResultMain)
    } else {
        getProfilePicture("id") {

            getADvertise(onResultMain)
        }
    }

}
//onResultMain -> call back used to get result when they are ready ;) 

【讨论】:

    【解决方案2】:

    使用call.execute()(即同步改造请求suspend协程函数。

     suspend fun <T> executeAsync(call: Call<T>, callback: Callback<T>) {
           try {
               if(foService.applicationProfileResponse != null) {
                  if(T is List<Advertising>) {
                     val advertisings = call.execute() // synchronous call 
                  } 
               }
           } catch(e : Exception) {
               // handle exception
           } 
     }
    

    或者, 添加

       .addCallAdapterFactory(CoroutineCallAdapterFactory())
    

    改造构建器并将.await()deferred一起使用

        call.await() // call is of type deferred 
    

    【讨论】:

      【解决方案3】:

      你可以这样用

      suspend fun <T> Call<T>.getResult(): T = suspendCoroutine { cont ->
          enqueue(object : Callback<T> {
              override fun onFailure(call: Call<T>, t: Throwable) {
                  cont.resumeWithException(t)
              }
      
              override fun onResponse(call: Call<T>, response: Response<T>) {
                  if (response.isSuccessful) {
                      cont.resume(response.body()!!)
                  } else {
                      cont.resumeWithException(ErrorResponse(response.message(), response.code()))
                  }
              }
      
          })
      }
      
      class ErrorResponse(message: String, code: Int) : Throwable(message )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-09-25
        • 2020-01-10
        • 2021-02-01
        • 1970-01-01
        • 2021-01-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多