RxJava也能像協程那樣優雅的請求網絡

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章