RxJava也能像協程那樣優雅的請求網絡
Retrofit&Coroutines 與 Retrofit&RxJava
兩年沒寫過文章了,一時之間不知道說些什麼好…嗶…
網絡請求框架相信在座的各位都能巴拉巴拉的說出一大堆,但是,我今天要說的不是網絡請求框架,今天就來說說被大家吹捧的協程以及比大家拋棄的RxJava的區別吧,以及RxJava到底能不能像協程那樣方便快捷。
本篇基於Retrofit2.9.0進行,與之前版本的請求方式略有差別,請注意。
一、創建Retrofit
第一步,Retrofit的配置
object RetrofitManager {
fun <K> create(clazz: Class<K>) : K = getRetrofit().create(clazz)
private fun getRetrofit(): Retrofit {
// 獲取retrofit的實例
return Retrofit.Builder()
//url必須以 ‘/’ 結尾,不然會報IllegalArgumentException
.baseUrl(ApiService.BASE_URL)
.client(getOkHttpClient())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
}
private fun getOkHttpClient(): OkHttpClient {
//添加一個log攔截器,打印所有的log
val httpLoggingInterceptor = HttpLoggingInterceptor()
//可以設置請求過濾的水平,body,basic,headers
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
return OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor) //日誌,所有的請求響應度看到
.connectTimeout(15L, TimeUnit.SECONDS)
.readTimeout(15L, TimeUnit.SECONDS)
.writeTimeout(15L, TimeUnit.SECONDS)
.proxy(Proxy.NO_PROXY)
.build()
}
}
二、創建Service接口
本次演示所用的接口來源與 玩android
interface ApiService {
companion object{
const val BASE_URL = "https://www.wanandroid.com/"
}
@GET("article/list/{page}/json")
fun getList(
@Path("page") page: Int
) : Observable<HomeListBean>
@GET("banner/json")
fun getBannerList(): Call<BannerBean>
@POST("postxxx/xxx")
fun postMsg(@Body json : RequestBody) : Observable<BaseBean>
}
好了,我們先回顧下傳統的RxJava+Retrofit進行的網絡請求。
首先對RxJava進行封裝,雖然可能和你們封裝的不同但也大同小異。
fun <T : Basexxx> request(){
check: Boolean = true,
function: Function<Boolean, ObservableSource<T>>,
next: Consumer<T>,
error: Consumer<Throwable> = Consumer {
finishRefreshorLoadData = true
errorLiveData.postValue(it)
}
) {
val disposable = Observable.just(check)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.filter { t -> t }
.flatMap(function)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(next, error)
addSubscription(disposable)
}
然後請求可能是這樣
request(
function = Function<Boolean, ObservableSource<xxxBen>>{
//網絡請求
model.getList(1)
},
next = Consumer{
liveDataVideo.postValue(it)
}
)
沒了解協程之前,這樣乍看也沒什麼毛病,中規中矩的,就是不夠簡便。
再看看協程的網絡請求:
GlobalScope.launch {
val bean = model.getBannerList().await()
liveData.postValue(bean)
}
吸(猛的倒吸吸一口氣)。
因爲Retrofit是2.9版本的,協程直接封裝在Retrofit的內部,作爲開發者的我們並不需要去過多的封裝,直接就可以進行調用。
兩者對比,孰優孰劣一目瞭然。協程既然能做到這麼簡便,那RxJava可不可以呢?
三、RxJava重封裝
肯定可以啦。
首先我們來分析下網絡請求我們所關心的部分:
1.直接處理請求成功結果
2.不關心請求異常
3.界面銷燬取消現有的請求
最終封如下,由擴展函數實現
fun <T : BaseBean> Observable<T>.onResult(
next : (T) -> Unit
){
this onResult Consumer {
//這裏進行返回判斷,與後臺協定
if (!TextUtils.equals(it.errorCode,"200")){
errorLiveData.value = it.errorMsg
return@Consumer
}
next(it)
}
}
看看最終的使用結果
fun getList(){
//RxJava
model.getList(0).onResult {
//做點其他的事情
//省略其他邏輯代碼
liveData2.value = it
}
}
fun getBannerList(){
//協程
GlobalScope.launch {
val bean = model.getBannerList().await()
liveData.postValue(bean)
}
}
嗯,這就很完美了。如果你的數據請求回調除了post數據之外,沒有其他的操作那還可以更加的簡單點:
fun getList(){
//RxJava
//沒有其他邏輯,直接返回數據
model.getList(0).onResult(liveData2::setValue)
}
協程雖然在網絡請求有獨特之處可以替換RxJava,但RxJava的流式編程也不是協程所能替代的,孰優孰劣難以定義。不知道你喜歡那種呢。
結尾附上RxJava的封裝代碼(Kotlin)
open class BaseViewModel : ViewModel(),IViewModel {
val errorLiveData: MutableLiveData<String> = MutableLiveData()
private var compositeDisposable = CompositeDisposable()
override fun onCreate(owner: LifecycleOwner) {
//創建
}
override fun onDestroy(owner: LifecycleOwner) {
//銷燬
detachView()
//移除生命週期監聽觀察者
owner.lifecycle.removeObserver(this)
}
override fun onLifecycleChanged(owner: LifecycleOwner, event: Lifecycle.Event) {
//生命週期狀態改變
}
//泛型可以爲 <T : BaseBean> ,也可以爲 <T : List<BaseBean>>
//此處爲Observable的拓展函數,你也可以改爲Flowable的拓展函數
fun <T : BaseBean> Observable<T>.onResult(
next: Consumer<T>,
error: Consumer<Throwable> = Consumer {
errorLiveData.postValue(it.message)
}
) {
val disposable = this.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(next, error)
addSubscription(disposable)
}
private infix fun <T : BaseBean> Observable<T>.onResult(
next: Consumer<T>
) {
this.onResult(next,Consumer {
errorLiveData.postValue(it.message)
})
}
fun <T : BaseBean> Observable<T>.onResult(
next : (T) -> Unit
){
this onResult Consumer {
//這裏進行返回判斷
/*if (!TextUtils.equals(it.errorCode,"200")){
errorLiveData.value = it.errorMsg
return@Consumer
}*/
next(it)
}
}
private fun addSubscription(disposable: Disposable) {
compositeDisposable.add(disposable)
}
private fun detachView() {
//保證activity結束時取消所有正在執行的訂閱
if (!compositeDisposable.isDisposed) {
compositeDisposable.clear()
}
}
}
Java版
某同學:雖然你說的很好,但是我用的是Java哦,Java有沒有辦法實現呢?
博主:額。。。這。。。
先看看kotlin的拓展方法轉成Java是什麼樣子的,大概如下:
由此可以看出:
kotlin: (Class)類.Function() 等價於
java的 Function(對象)
等式:Class.(參數…,Function…) ==> Function(對象,參數…,Function…)
…
所以,以上的代碼都可以轉換成這樣
//Java版
public class BaseViewModel extends ViewModel implements IViewModel {
public boolean finishRefreshorLoadData = true;
public MutableLiveData<Throwable> errorLiveData = new MutableLiveData<>();
private CompositeDisposable compositeDisposable = new CompositeDisposable();
@Override
public void onCreate(@NotNull LifecycleOwner owner) {
}
@Override
public void onDestroy(@NotNull LifecycleOwner owner) {
detachView();
owner.getLifecycle().removeObserver(this);
}
@Override
public void onLifecycleChanged(@NotNull LifecycleOwner owner, @NotNull Lifecycle.Event event) {
}
public <T extends BaseBean> void onResult(
Observable<T> observable,
Consumer<T> next,
Consumer<Throwable> error
){
Disposable disposable = observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(next, error);
compositeDisposable.add(disposable);
}
public <T extends BaseBean> void onResult(
Observable<T> observable,
Consumer<T> next
){
onResult(observable,next,throwable -> {
errorLiveData.postValue(throwable);
});
}
private void detachView() {
//保證activity結束時取消所有正在執行的訂閱
if (!compositeDisposable.isDisposed()) {
compositeDisposable.clear();
}
}
}
具體引用如下:
public class JavaViewModel extends BaseViewModel{
private MainModel model = new MainModel();
public MutableLiveData<HomeListBean> liveData = new MutableLiveData<>();
public void getList(){
onResult(model.getList(0),homeListBean -> {
//做點其他事情
liveData.setValue(homeListBean);
});
//沒有其他操作,直接放回數據
onResult(model.getList(0),liveData::setValue);
}
}
某同學:看起來好像沒kotlin方便哦。
我:。。。
項目地址
因爲篇幅所限,本文只說RxJava的封裝,詳細代碼就不貼出來了,附上demo的地址,有興趣的可以去看看。
github: https://github.com/cithrf/RxDemo