Retrofit+RxJava总结

作者: sunbinqiang | 来源:发表于2016-11-23 11:26 被阅读543次

    上次发布的文章Retrofit2.0+RxJava初步 简单介绍了Retrofit2.0与RxJava结合使用, 前段时间在公司的项目中引入了Retrofit框架,今天对项目中碰到的问题和一些常见的使用场景做个总结。

    本文主要包含以下2个内容:

    • HttpResult统一处理返回结果
    • 网络请求与生命周期绑定
    • retrofit缓存及日志

    统一处理返回结果

    在项目中一般与后台定义好返回的json数据结构, 我们的格式如下:

    {
        "code": 0,
        "data": ...,
        "msg": "success"
    }
    

    所以,在项目的service层中,统一判断code值, 如果错误, 抛出异常, 如果正确,再把data结构返回给上层界面层处理。

    首先,定义一个HttpResult对象:

    public class HttpResult<T> {
        public int code;
        public String msg;
        public T data;
    }
    

    第二步, 我们在上一篇文章可以看到, 定义一个请求的方法如下:

    @GET("shots/{id}/comments")
    Observable<HttpResult<Comments>> getComments(@Path("id") int id, @Query("page") String page);
    

    对于如何将HttpResult对象,进行解析,成功的话传回Comments结构, 失败则抛出异常进行处理。
    这里需要用到RxJava的一个功能: map() , 它是RxJava的一个核心功能之一, 这个方法提供了对事件序列进行变换的支持, 也就是说它可以将传入的一个对象转化成另外一个对象, 它的参数是一个“FunX”(x是数字,代表有几个参数)。
    例如想要实现HttpResult<T> --> T 的转换, 我们是这样实现的:

    private class HttpResultFunc<T> implements Func1<HttpResult<T>, T> {
    
        @Override
        public T call(HttpResult<T> httpResult) {
            if (httpResult == null) {
                throw new HttpException(400, "网络错误");
            } else if (!httpResult.isSuccess()) {
                String msg = httpResult.msg != null ? httpResult.msg : "未知错误";
                throw new HttpException(httpResult.code, msg);
            } else
                return httpResult.data;
        }
    }
    

    有了上面的转化, 为了上面的转化适用于所有的接口请求, 我们得将上述转化用transform封装起来, 然后使用compose将Observable自身进行变化, 代码如下:

    private final HttpResultFunc<Object> func = new HttpResultFunc<>();
    
    private class RetrofitTransformer implements Transformer {
    
        @Override
        public Object call(Object o) {
            Observable observable = ((Observable) o);
            return observable.subscribeOn(io())
                    .unsubscribeOn(io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .onErrorReturn(errorFunc)
                    .map(func);
        }
    }
    
    protected <T> Observable.Transformer<HttpResult<T>, T> lifts() {
        return (Observable.Transformer<HttpResult<T>, T>) transformer;
    }
    

    最后, 完成Observable的转化:

    return ApiService.getComments(id, page).compose(this.<Comments>lifts());
    

    生命周期绑定

    · 内存泄漏的问题
    当我们在调用网络请求的时候, 会生成一个subscription对象, 如果里面引用了context或者其他的view对象, 而在Activity生命周期结束的时候又没有及时结束, 那么很容造成内存泄漏的问题。
    这里用了CompositeSubscription解决这个问题:

    mCompositeSubscription = new CompositeSubscription();
    
    public void addSubscription(Subscription subscription) {
        if (mCompositeSubscription != null && mCompositeSubscription.isUnsubscribed()) {
            mCompositeSubscription.add(subscription);
        }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mCompositeSubscription != null && !mCompositeSubscription.isUnsubscribed()) {
            mCompositeSubscription.unsubscribe();
            mCompositeSubscription = null;
        }
    }
    

    上述代码在基类BaseActivity中实现, 每次调用网络请求的时候,都通过addSubscription() 方法。

    缓存及日志

    retrofit缓存及日志功能网上代码比较多了, 就简单说一下,都是通过拦截器实现的 :

    • 缓存
    private static Interceptor cacheInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            if (!HttpUtils.isNetworkConnected()) {
                request = request.newBuilder()
                        .cacheControl(CacheControl.FORCE_CACHE)
                        .build();
            }
            Response response = chain.proceed(request);
            if(HttpUtils.isNetworkConnected()){
                //有网的时候读接口上的@Headers里的配置
                String cacheControl = request.cacheControl().toString();
                return response.newBuilder()
                        .header("Cache-Control", cacheControl)
                        .removeHeader("Pragma")
                        .build();
            }else{
                return response.newBuilder()
                        .header("Cache-Control", "public, only-if-cached, max-stale=2419200")
                        .removeHeader("Pragma")
                        .build();
            }
        }
    };
    

    添加缓存拦截的时候, 最好用两种方式都添加一下, 仅仅是addInterceptor是不够的:

    .addNetworkInterceptor(cacheInterceptor)   
    .addInterceptor(cacheInterceptor)
    

    日志缓存代码略长, 可以看我项目中的实现:代码

    相关文章

      网友评论

      • 28b4f01d4713:写得真好!
        public void addSubscription(Subscription subscription) {
        if (mCompositeSubscription != null && mCompositeSubscription.isUnsubscribed()) {
        mCompositeSubscription.add(subscription);
        }
        }
        中的mCompositeSubscription.isUnsubscribed()前面应该加!吧?

      本文标题:Retrofit+RxJava总结

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