Kotlin協(xié)程 ----- suspendCoroutine和suspendCancellableCoroutine的使用

Android_Banner.jpg

簡介

  • suspendCoroutine 的使用
  • suspendCancellableCoroutine的使用
  • Retrofit是如何支持協(xié)程的

suspendCoroutine 的使用

這里我們將使用suspendCoroutine將單一方法的接口方法改造成具有返回值的方法

單一方法的回調(diào)

聲明一個單一方法的接口

/**
 * @author : zhangqi
 * @time : 6/22/21
 * desc : 單一方法接口
 */
interface SingleMethodCallback {

    fun onCallBack(value: String)
}

接著模擬一個耗時的操作,當(dāng)操作完畢我們把結(jié)果回調(diào)給實現(xiàn)類

  /**
   * 模擬一個耗時操作
   */
private fun runTask(callback: SingleMethodCallback) {
        thread {
            Thread.sleep(1000)
            callback.onCallBack("result")
        }
    }

最后我們調(diào)用runTask方法,傳入SingleMethodCallback的實現(xiàn)

private fun runTaskDefault() {
        runTask(object : SingleMethodCallback {
            override fun onCallBack(value: String) {
                TODO("Not yet implemented")
            }
        })
    }

接著我們使用Kotlin協(xié)程提供的 suspendCoroutine 讓runTaskDefault具有返回值;

改造一下runTaskDefault ---> runTaskWithSuspend

 suspend fun runTaskWithSuspend(): String {
        // suspendCoroutine是一個掛起函數(shù)
        return suspendCoroutine { continuation ->
            runTask(object : SingleMethodCallback {
                override fun onCallBack(value: String) {
                    continuation.resume(value)
                }
            })
        }
    }

這里 suspendCoroutine是一個掛起函數(shù),掛起函數(shù)只能在協(xié)程或者其他掛起函數(shù)中被調(diào)用,同時我們在回調(diào)中將結(jié)果值傳入到Coutination的resume方法中;

經(jīng)過我們上述的操作將回調(diào)方法具有返回值了;

suspendCancellableCoroutine 的使用

Success And Failure 類別的接口

聲明 success and failure 類型的接口

/**
 * @author : zhangqi
 * @time : 6/22/21
 * desc :
 */
interface ICallBack {
    fun onSuccess(data: String)
    fun onFailure(t: Throwable)
}

同樣我們模擬一個耗時操作,在獲取結(jié)果的時候 調(diào)用 onSuccess()將結(jié)果回調(diào)給實現(xiàn),出現(xiàn)錯誤調(diào)用onFailure將錯誤交給實現(xiàn)處理

 /**
   * 模擬一個耗時操作
   */
 private fun request(callback: ICallBack) {
   thread {
     try {
       callback.onSuccess("success")
     } catch (e: Exception) {
       callback.onFailure(e)
     }
   }
 }

最后我們調(diào)用requet方法,傳入接口的實現(xiàn),

private fun requestDefault() {
  request(object : ICallBack {
    override fun onSuccess(data: String) {
      // doSomething
    }

    override fun onFailure(t: Throwable) {
      // handle Exception
    }

  })
}

同樣我們使用Kotlin協(xié)程提供的掛起函數(shù)將 requestDefault()改造成 具有返回值的函數(shù) requestWithSuspend()

只不過我們這里使用了 suspendCancellablkeCoroutine ,代碼上見吧!

 private suspend fun requestWithSuspend(): String {
        return suspendCancellableCoroutine { cancellableContinuation->
            request(object : ICallBack {
                override fun onSuccess(data: String) {
                    cancellableContinuation.resume(data)
                }

                override fun onFailure(t: Throwable) {
                    cancellableContinuation.resumeWithException(t)
                }
            })
        }
    }

suspendCancellableCoroutine 是一個掛起函數(shù),我們將requestWithSuspend聲明稱掛起函數(shù)

在onSucess()中我們我們調(diào)用CancellableContinue # resume 方法將結(jié)果返回,在onFailure調(diào)用CancellableContinuation # resumeWithException 將異常傳入進(jìn)去;

調(diào)用requestWithSuspend()

private fun runRequestSuspend() {
  try {
    viewModelScope.launch {
      val value = requestWithSuspend()
    }
  } catch (e: Exception) {
    e.printStackTrace()
  }
}

在ViewModel中Kotlin協(xié)程提供了 viewModelScope 來開啟一個協(xié)程,改協(xié)程是具有聲明周期的與當(dāng)前ViewModel保持一致;

這里我們使用了try{}catch 將我們開啟的協(xié)程處理了下,調(diào)用成功獲取到value值,出現(xiàn)錯誤我們在catch塊中除了一下;

以上就是 我們兩種日常遇見頻率較高的情況進(jìn)行的改造(回調(diào)方法具有返回值)

Retrofit是如何支持協(xié)程的

Retrofit是在2.6版本開始支持,我們先對比下使用協(xié)程前后的區(qū)別

使用協(xié)前
/**
  * 發(fā)現(xiàn)頁面的數(shù)據(jù)
  */
@GET("/api/v7/index/tab/discovery")
fun getDiscoveryData(): Call<OpenEyeResponse>

// 在ViewModel中調(diào)用
 /**
   * 沒有使用協(xié)程做網(wǎng)絡(luò)請求
   */
    fun getDiscoverData() {
      WidgetService.openEyeInstance.getDiscoveryData().enqueue(object : Callback<OpenEyeResponse> {
        override fun onResponse(call: Call<OpenEyeResponse>, response: Response<OpenEyeResponse>) {
          var body = response.body()
        }

        override fun onFailure(call: Call<OpenEyeResponse>, t: Throwable) {
        }
      })
    }

接著我們看下使用協(xié)程后

使用協(xié)程后
/**
  * 通過協(xié)程做本次請求
  * @return OpenEyeResponse
  */
@GET("/api/v7/index/tab/discovery")
suspend fun getDiscoveryDataCoroutine(): OpenEyeResponse

  /**
   * 使用協(xié)程做的請求
   */
fun getDiscoverDataWithCoroutine() {
  try {
    viewModelScope.launch {
      var discoveryDataCoroutine = WidgetService.openEyeInstance.getDiscoveryDataCoroutine()
    }
  } catch (e: Exception) {
  }
}

可以看見,在接口類中聲明的方法聲明為掛起函數(shù),同時我們可以將我們想要的數(shù)據(jù)結(jié)構(gòu)直接返回不用Call包一層;

Retrofit支持協(xié)程

Retrofit # HttpServiceMethod

 okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
      // 當(dāng)是直接返回數(shù)據(jù)結(jié)構(gòu)走這里
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>)
            // 執(zhí)行了 SuspendForResponse
          new SuspendForResponse<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForBody<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
              continuationBodyNullable);
    }

SuspendForResponse ---> KotlinExtensions.awaitResponse

SuspendForResponse(
        RequestFactory requestFactory,
        okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, Call<ResponseT>> callAdapter) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }

    @Override
    protected Object adapt(Call<ResponseT> call, Object[] args) {
      call = callAdapter.adapt(call);
      //noinspection unchecked Checked by reflection inside RequestFactory.
      Continuation<Response<ResponseT>> continuation =
          (Continuation<Response<ResponseT>>) args[args.length - 1];

      // See SuspendForBody for explanation about this try/catch.
      try {
        // 在這里直接調(diào)用了 KotlinExtensions.awaitResponse
        return KotlinExtensions.awaitResponse(call, continuation);
      } catch (Exception e) {
        return KotlinExtensions.suspendAndThrow(e, continuation);
      }
    }

KotlinExtensions.awaitResponse

suspend fun <T> Call<T>.awaitResponse(): Response<T> {
  // 在這里使用了suspendCancellableCoroutine
  return suspendCancellableCoroutine { continuation ->
     // 當(dāng)我們開啟的協(xié)程開啟了之后,會回調(diào)到這個方法
     // 取消當(dāng)前的請求
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        // 當(dāng)成功拿到response之后 將response返回
        continuation.resume(response)
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        // 失敗的話 直接將異常拋出
        continuation.resumeWithException(t)
      }
    })
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容