当Retrofit遇上协程

作者: SimpleFunc | 来源:发表于2019-10-23 17:20 被阅读0次

    Retrofit 2.6之后的版本本身支持了使用Kotlin的协程。使用起来更加简洁。

    在2.6之前的版本中如果要使用协程可以添加coroutines-adapter来使用。例如JakeWharton大神编写的retrofit2-kotlin-coroutines-adapter
    可以看到该项目已经标识为DEPRECATED。推荐升级到2.6之后的版本

    2.6版本之前的写法

    接口声明

    @GET("test")
    fun test() : Deferred<TestResponse>
    

    添加adapter

    Retrofit.Builder()
                .baseUrl(NetworkConfig.baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(CoroutineCallAdapterFactory.create())
                .build()
    

    调用:

    GlobalScope.launch(Dispatchers.IO){
          val response = test().await()
          ....
     }
    

    2.6版本之后

    2.6版本之后不再需要设置Adapter,直接使用Retrofit自身的方式

    接口声明

     @GET("test")
    suspend fun test() : Response<TestResponse>
    // 或者
     @GET("test")
    suspend fun test() : Call<TestResponse>
    

    调用

     GlobalScope.launch(Dispatchers.IO){
          try{
              val response = test()
              if(response.isSuccessful){
                   ...
              }else{
                  ...
              }
          }catch(e: Throwable){
               ... 
          }
     }
    
    //Call
     GlobalScope.launch(Dispatchers.IO){
          test().enqueue(object : Callback<TestResponse>{
                override fun onFailure(call: Call<TestResponse>, t: Throwable) {
                      ...
                }
                override fun onResponse(call: Call<TestResponse>, response: Response<TestResponse>){
                    ...         
                }
            }
     }
    

    使用协程最大的好处就是可以避免回调,使异步逻辑更加简洁,清晰。以上的写法已经比RxJava或其他版本要直观了。

    如果你使用了官方的MVVM架构,并且升级到了Android X,可以更加简洁。

    添加Kotlin扩展依赖

    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-beta01'
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-beta01'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-beta01'
    

    接口声明可以更直接

    suspend fun test() : TestResponse
    

    调用接口直接转LiveData

     val liveData: LiveData<TestResponse> = liveData(Dispatchers.IO){
          val response = test()
          emit(response)
    }
    

    这样这个LiveData就直接把接口返回的数据给接受了。

    状态处理

     val liveData: LiveData<TestResponse> = liveData(Dispatchers.IO){
            emit(State.Loading)
            try {
                val data = test()
                emit(State.Success(data))
            }catch (e: Exception){
                emit(State.Error(e))
            }
    }
    

    ViewModelScope

    ViewmModel也添加了一个扩展ViewModelScope

    viewModelScope.launch {
               val data = test()
               ...
     }
    

    LifecycleScope

    LifecycleScope是lifecycleOwner的一个扩展。

    class TestFragment: Fragment() {
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            viewLifecycleOwner.lifecycleScope.launch {
                   val data = test()
                   ...
            }
        }
    }
    

    LifecycleScope中可以指定执行的生命周期节点

    lifecycleScope.launch {
            //在Activity onStart之后执行
            whenStarted {
                    val data = test()
                    ...
            }
    }
    

    LiveDataScope, ViewModelScope和lifecycleScope会自动处理自身的生命周期,在生命周期结束时会自动取消没有执行完成的协程任务,但是使用lifecycleScope应该注意,因为lifecycleScope可以指定执行的时机,而在lifecycleScope一般执行的都是异步的任务,到异步任务执行完成之后lifecycle.state是不确定的。如下面的例子:

    lifecycleScope.launchWhenStarted {
                try {
                     ...
                } finally {
                   ...
                }
            }
    

    在finally中的代码执行时可能lifecycle.state已经是DESTROYED,如果在执行一些和生命周期相关的操作就需要判断当前的状态。如在DESTROYED之后再执行UI相关的操作可能会引起错误,甚至闪退。

    如果现在还没升级到Android X,又使用了MVVM相关的组件,可以自己添加扩展的形式来实现,如LiveData:

    inline fun <T: Any> ViewModel.liveData(
            context: CoroutineContext = Dispatchers.IO,
            crossinline block: suspend () -> T): LiveData<State<T>>{
        val liveData = MutableLiveData<RequestResult<T>>()
        val job = CoroutineScope(context).launch {
            liveData.postValue(State.Loading)
            try {
                val value = block()
                liveData.postValue(State.Success(value))
            }catch (e: Exception){
                liveData.postValue(State.Error(e))
            }
        }
        //需要自己处理Job的生命周期
        collectJob(job)
        return liveData
    }
    

    需要注意的是要自己处理协程的生命周期,避免内存泄漏或出现错误。

    相关文章

      网友评论

        本文标题:当Retrofit遇上协程

        本文链接:https://www.haomeiwen.com/subject/erphvctx.html