阅读 5562

Android Kotlin Flow + 协程 + Retrofit + MVVM优雅的实现网络请求(简洁!!!)

最近学习了kotlin的flow,感觉和RxJava很像 于是就利用它来封装网络请求。再之前的文章中我也封装过网络请求juejin.cn/post/692263…,但是使用了flow  发现更简单。

封装的部分部分,很简单

suspend fun <T> requestFow(  
   showLoading: Boolean = true, 
   request: suspend ApiInterface.() -> BaseResponse<T>?
):Flow<BaseResponse<T>> { 
   if (showLoading) {  
      showLoading()   
 }   
 return flow {      
  val response = request(Api) ?: throw IllegalArgumentException("数据非法,获取响应数据为空")  
      if (response.errorCode != 0) {  
          throw  ApiException(response.errorCode, response.errorMsg ?: "")   
     }       
 emit(response)    
}.flowOn(Dispatchers.IO) 
.onCompletion {  
  closeLoading()   
 }
}
复制代码

就这么简单 就全部完成封装了

Api:就是Retrofit的ApiInterface对象 

emit: 就是把网络请求的结果回调出去

flowOn:指定运行的线程 主要是flowOn 代码前面的线程

onCompletion:就是请求结束的回调,包括成功和失败 这里关闭了请求对话框

然后调用collect方法获取emit回调出的结果 就是网络请求的结果

fun requestFlowData(){ 
   viewModelScope.launch {  
      requestFow{     
       getListProject();     
       }.catch { cause ->         
        run {               
               Log.e("requestFow","==requestFow==cause==vm==>${cause}")   
             when (cause) {      
              is ApiException->{      
              }                 
              is IOException->{     
                }         
              else->{    }            
    
               } 
      }      
    }.collect {   
         responseTextFlow.set(it.data.toString())  
      }   
 }
}
复制代码

requestFlowData方法就是调用网络请求,这个是再Android中的viewmodel中调用的 

因为方法调用再viewModelScope.launch 中 所以catch方法和collect方法 还有onCompletion方法 都是主线程中

requestFlow:就是封装的网络请求方法

catch: 捕获的是所有的异常,包括自定义的异常,这里我抛出了一个自定义的Api异常

collect:就是获取我们想要的数据

好了 一个网络请求就封装完毕了 就这么简洁

不过还有一个问题,就是捕获异常的问题,上面封装再调用的时候是每次都要加catch方法,如果不加则出现错误就会崩溃,比如IO异常,Api异常等。既然每次都要加的话,那我就把catch方法放在封装的内部调用了,就再onComplete方法后面加上catch,当然也可以onComplete方法前面加上,不同位置调用会不一样的效果 具体请看juejin.cn/post/684490…

当我加完之后,发现再调用的时候我用catch方法单独获取异常,发现单独获取异常的catch方法不执行了,看来catch只捕获一次异常,后面的方法就不会调用了。所以只能再catch和onComplete方法之前获取了,而且发现onComplete方法里面也能获取到异常

后来我想到了这个方法,封装的部分还是封装到onComplete,并且在onComplete方法内增加了对异常默认处理,就是简单的土司,可以二次加工异常再抛出去

suspend fun <T> requestFow(  
      showLoading: Boolean = true,       
 request: suspend ApiInterface.() -> BaseResponse<T>?   
 ): Flow<BaseResponse<T>> { 
       if (showLoading) {       
           showLoading()       
        }      
       return flow { 
           val response = request(Api) ?: throw IllegalArgumentException("数据非法,获取响应数据为空") 
           if (response.errorCode != 0) {              
            throw  ApiException(response.errorCode, response.errorMsg ?: "")       
            }           
            emit(response)       
          }.flowOn(Dispatchers.IO)            
          .onCompletion { cause ->    
            run {            
                closeLoading()      
                Log.e("requestFow", "==onCompletion==cause==>${cause}")       
                cause?.let {                      
                      toast(it.message?:"")
                     // throw it //可以对异常二次加工 然后抛出去        
            }           
     }         
   }  
 }
复制代码

那这时候要捕获异常,难道又要每次调用的时候加catch方法?答案:当然不是!

这里我对collect方法下手,因为collect是每次必须调用的,你不调用你都获取不到数据,

既然它也是每次调用,那就把collect和catch方法放在一起,于是定义了一个这样的方法

suspend fun <T> Flow<T>.next(bloc: suspend T.() -> Unit): Unit = catch {  }.collect {    bloc(it)}
复制代码

next方法,只要调用了next方法就代表捕获异常和回调数据,这样有异常的化那next方法中的catch方法就已经捕获了,那要获取捕获的异常怎么办,其实就是还是用flow的catch捕获就行了,因为这时候catch是再next方法前面调用的会最先捕获到异常。调用的时候只是collect变成了next方法,变成如下这样了

requestFow() { 
   getListProject()
}.catch { 
   cause -> Log.e("requestFow", "==catch=========>${cause}")
}. next{   
 Log.e("requestFow", "==collect=---------------=next==>${this}")   
 responseTextFlow.set(data.toString())
}
复制代码

这样就顺畅多了,如果需要异常单独处理就调用的时候加catch ,不需要就不调用 ,这时候出异常了走next方法的catch。然后异常的默认统一处理再onComplete方法里面

详细的就不多说了 看demo吧,github地址:

github.com/wangxiongta…

欢迎提供意见建议

文章分类
Android
文章标签