在Retrofit中加入RxJava

这篇文章的由来

目前项目中引入了RxJava,而我在其他项目里分别使用过Retrofit以及Retrofit+ RxJava。以自己的感受而言,RxJava确实很强大,而且对于编码效率和代码简洁性有不小的提升,所以简单对比一下两者的区别。本文默认读者已熟悉RxJava的基本Api以及使用场景,若非如此,则参考该文章:http://gank.io/post/560e15be2dca930e00da1083

干货

基本使用

在目前的项目里,使用RetrofitRetrofit + RxJava的场景几乎类似,代码上有一点区别:

  1. StickerServerApi中写下接口方法:
// 使用Retrofit
@GET("path")
Call<T> apiName(@QueryMap Map<String, String> params);
// 使用Retrofit + RxJava
@GET("path")
Observable<T> apiName(@QueryMap Map<String, String> params);
  1. WebService中,调用StickerServerApi中的方法:
// 使用Retrofit
@MainThread
public void requestApi(Callback<T> callback) {
    Map<String, String> params = getParamsPacker().pack();
    Call<T> call = mStickerServerApi.apiName(params);
    call.enqueue(callback);
}
// 使用Retrofit + RxJava
public Observable<T> requestApi() {
    Map<String, String> params = getParamsPacker().pack();
    return mStickerServerApi.apiName(params);
}
  1. Repository中,调用WebService中的方法,并处理响应逻辑:
// 使用Retrofit
public LiveData<Resource<T>> requestApi() {
    getWebService().requestApi(new Callback<T>() {
        @Override
        public void onResponse(Call<T> call, Response<T> response) {
            mLiveData.setValue(Resource.success(response.body());
        }

        @Override
        public void onFailure(Call<T> call, Throwable t) {
            mLiveData.setValue(Resource.error(t.message);
        }
    });
    mLiveData.setValue(Resource.loading(null));
    return mLiveData;
}
// 使用Retrofit + RxJava
public LiveData<Resource<T>> requestApi() {
    getWebService().requestApi()
                // 在子线程调用接口
                .subscribeOn(Schedulers.io())
                // 在主线程调用回调方法
                .observeOn(AndroidSchedulers.mainThread())
                // subscribe方法针对不同的参数有很多的重载方法
                // 下文传入Observer对象的重载方法是相对回调最多的,所以看起来似乎比上面的代码更复杂
                // 实际使用中可以使用其他重载方法以传入更为简单的接口对象
                .subscribe(new Observer<BaseData<List<StarResponseModel>>>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        // 在订阅时触发,这里可以不作处理
                    }

                    @Override
                    public void onNext(T t) {
                        // 相当于onResponse,和onError互斥触发
                        mLiveData.setValue(Resource.success(t);
                    }

                    @Override
                    public void onError(Throwable e) {
                        // onFail,和onNext互斥触发
                        mLiveData.setValue(Resource.error(t.message);
                    }

                    @Override
                    public void onComplete() {
                        // 在onNext或者onError后触发,可以不作处理
                    }
                })
    mLiveData.setValue(Resource.loading(null));
    return mLiveData;
}
  1. View层进行liveData的订阅,处理相应的UI变化。这部分的代码没有区别,不再赘述。

复杂情景中的使用

这部分与基本使用中的代码区别在于Repository中的逻辑,所以只贴出Repository中的代码。

  1. 接口的循环调用。某些场景下,需要先请求接口1获取响应数据,并作为接口2的请求参数,而最终的逻辑需要在接口2的响应方法中执行。在项目中也有这样的情景,比如上传资源文件到阿里云后台,然后将地址发送给服务器。
// 使用Retrofit
public LiveData<Resource<T>> requestApi() {
    getWebService().requestApi1(new Callback<T>() {
        @Override
        public void onResponse(Call<T> call, Response<T> response) {
            // api1的返回值是api2的请求参数
            T t = response.body();
            getWebService().requestApi2(t, new Callback<K>() {
                @Override
                public void onResponse(Call<K> call, Response<K> response) {
                    mLiveData.setValue(Resource.success(response.body());
                }

                @Override
                public void onFailure(Call<K> call, Throwable t) {
                    mLiveData.setValue(Resource.error(t.message);
                }
            });
        }

        @Override
        public void onFailure(Call<T> call, Throwable t) {
            mLiveData.setValue(Resource.error(t.message);
        }
    });
    mLiveData.setValue(Resource.loading(null));
    return mLiveData;
}
// 使用Retrofit + RxJava
public LiveData<Resource<T>> requestApi() {
    getWebService().requestApi1()
                // 在子线程调用接口
                .subscribeOn(Schedulers.io())
                .flatMap(new Function<T, ObservableSource<K>>() {
                    @Override
                    public ObservableSource<K> apply(T t) throws Exception {
                        // api1的返回值是api2的请求参数
                        return getWebService().requestApi2(t);
                    }
                })
                // 在主线程调用回调方法
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<K>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        // 在订阅时触发,这里可以不作处理
                    }

                    @Override
                    public void onNext(K k) {
                        // 相当于onResponse,和onError互斥触发
                        mLiveData.setValue(Resource.success(k);
                    }

                    @Override
                    public void onError(Throwable t) {
                        // onFail,和onNext互斥触发
                        mLiveData.setValue(Resource.error(t.message);
                    }

                    @Override
                    public void onComplete() {
                        // 在onNext或者onError后触发,可以不作处理
                    }
                })
    mLiveData.setValue(Resource.loading(null));
    return mLiveData;
}

可以看到,在Retrofit中,想要实现接口套接口的逻辑,需要两层Callback嵌套,而如果加入了RxJava,则只需要调用flatMap()就可以了。前者的多层嵌套相比后者无论从开发效率还是代码可读性而言都要略逊一筹。而这只是两层嵌套,如果是多层嵌套,差距更显而易见。

  1. 在接收到响应数据后,进行一些异步操作。
    网络请求的回调方法通常需要进行UI更新,所以一般回调方法都是在主线程中运行。而如果这时需要进行一些异步操作,比如io读取、数据库存取,代码就会比较复杂。以下以下载视频为例。
// 使用Retrofit
public LiveData<Resource<File>> requestApi() {
    getWebService().requestApi(new Callback<Video>() {
        @Override
        public void onResponse(Call<Video> call, Response<Video> response) {
            Video video = response.body();
            final String url = video.url;
            final Handler handler = new Handler();
            new Thread() {
                @Override
                public void run() {
                    File file = new File("filePath");
                    // 写文件逻辑省略
                    ...  
                    // 切回 UI 线程
                    handler.post(new Runnable() { 
                        @Override
                        public void run() {
                            // 可能存在的其他逻辑省略
                            ...
                            // liveData赋值
                            mLiveData.setValue(Resource.success(file))
                        }
                    });
                }
            }).start();
        }

        @Override
        public void onFailure(Call<T> call, Throwable t) {
            mLiveData.setValue(Resource.error(t.message);
        }
    });
    mLiveData.setValue(Resource.loading(null));
    return mLiveData;
}
// 使用Retrofit + RxJava
public LiveData<Resource<File>> requestApi() {
    getWebService().requestApi()
                // 在子线程调用接口
                .subscribeOn(Schedulers.io())
                .map(new Function<T>, File>() {
                    @Override
                    public File apply(T t) throws Exception {
                        File file = new File("filePath");
                        // 省略写文件逻辑
                        ...
                        // 返回文件
                        return file;
                    }
                })
                // 切换回主线程
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<File>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        // 在订阅时触发,这里可以不作处理
                    }

                    @Override
                    public void onNext(File file) {
                        // 相当于onResponse,和onError互斥触发
                        // 其他逻辑
                        ...
                        // liveData赋值
                        mLiveData.setValue(Resource.success(file);
                    }

                    @Override
                    public void onError(Throwable t) {
                        // onFail,和onNext互斥触发
                        mLiveData.setValue(Resource.error(t.message);
                    }

                    @Override
                    public void onComplete() {
                        // 在onNext或者onError后触发,可以不作处理
                    }
                })
    mLiveData.setValue(Resource.loading(null));
    return mLiveData;
}

对比这一场景中的两部分代码,同样地,单独使用Retrofit时,动辄两三层的缩进,以及onResponse()方法中大量的代码,都会降低可读性和开发效率。在加入了RxJava后,只需要一个map()方法(还有其他有用的api,这里暂时不提),再加上RxJava的线程调度特性,就使代码看起来简单了许多。

总结

现在很多文章都推荐使用RxJavaRetrofit进行网络请求框架的搭建,而且Retrofit本身也对RxJava进行了支持,这就足够说明加入RxJava是有一定的意义的。而我自己的使用经验也使得我更偏向于结合RxJava来使用Retrofit。但这毕竟只是个工具,而且相对陌生,有一定的学习成本,并且开发效率受到熟练度的影响。但不论最终是否加入RxJava,多了解一下也是有好处的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容

  • 原文地址:http://gank.io/post/560e15be2dca930e00da1083 前言 我从去年...
    AFinalStone阅读 2,191评论 5 23
  • 转一篇文章 原地址:http://gank.io/post/560e15be2dca930e00da1083 前言...
    jack_hong阅读 913评论 0 2
  • 好久没来这里了,再来……已恍如隔世! 2019年2月18日,今天,是我人生最重要的分水岭! 过去的,不愿再去回头,...
    AmyMoonlight阅读 572评论 0 0
  • 神秘的尾周末,无比神秘,精神上的洗礼,更爱大家和死党
    陈琦不黑阅读 121评论 0 2
  • “小可,你长大以后想做什么呀?” “科学家!” 六岁时,面对七大姑八大姨挑逗式的提问,林可总会骄傲地仰起脸,不假思...
    安五香阅读 177评论 0 1