RxJava2+Retrofit2实现网络请求封装(三)

作者: Android_惜年 | 来源:发表于2017-05-23 15:11 被阅读688次

    经过前面的封装,我的网络请求库已经使用了很长一段时间了,但还是觉得写的代码多了点,而且还不支持缓存的问题.
    今天我将继续分享我的后续优化和缓存的解决方案.

    优化

    为了让在Activity和Fragment中发的网络请求自动绑定生命周期,而不需要每次都重复的写同一行代码,我修改了原来RxUtil类,下面我附上目前这个类的完整代码.

    public class RxUtil {
        public static <T> LifecycleTransformer<T> bindToLifecycle(LifecycleProvider provider) {
            if (provider instanceof RxAppCompatActivity) {
                return ((RxAppCompatActivity) provider).bindToLifecycle();
            } else if (provider instanceof RxFragment) {
                return ((RxFragment) provider).bindToLifecycle();
            } else {
                throw new IllegalArgumentException("class must extents RxAppCompatActivity or RxFragment");
            }
        }
    
        public static <T> LifecycleTransformer<T> bindToLifecycle(LifecycleProvider provider, ActivityEvent event) {
            if (provider instanceof RxAppCompatActivity) {
                return ((RxAppCompatActivity) provider).bindUntilEvent(event);
            } else {
                throw new IllegalArgumentException("class must extents RxAppCompatActivity");
            }
        }
    
        public static <T> LifecycleTransformer<T> bindToLifecycle(LifecycleProvider provider, FragmentEvent event) {
            if (provider instanceof RxFragment) {
                return ((RxFragment) provider).bindUntilEvent(event);
            } else {
                throw new IllegalArgumentException("class must extents RxFragment");
            }
        }
    
        public static <T> ObservableTransformer<T, T> applySchedulers(final LifecycleProvider provider) {
            return new ObservableTransformer<T, T>() {
                @Override public ObservableSource<T> apply(@NonNull Observable<T> upstream) {
                    return upstream
                            .subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .compose(RxUtil.<T>bindToLifecycle(provider));
    
                }
            };
        }
    
        public static <T> ObservableTransformer<T, T> applySchedulers(final LifecycleProvider provider, final ActivityEvent event) {
            return new ObservableTransformer<T, T>() {
                @Override public ObservableSource<T> apply(@NonNull Observable<T> upstream) {
                    return upstream
                            .subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .compose(RxUtil.<T>bindToLifecycle(provider, event));
    
                }
            };
        }
    
        public static <T> ObservableTransformer<T, T> applySchedulers(final LifecycleProvider provider, final FragmentEvent event) {
            return new ObservableTransformer<T, T>() {
                @Override public ObservableSource<T> apply(@NonNull Observable<T> upstream) {
                    return upstream
                            .subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .compose(RxUtil.<T>bindToLifecycle(provider, event));
    
                }
            };
        }
    
        public static <T> ObservableTransformer<T, T> applySchedulers(final LifecycleProvider provider, @NonNull final Dialog dialog) {
            return new ObservableTransformer<T, T>() {
                @Override public ObservableSource<T> apply(@NonNull Observable<T> upstream) {
                    return upstream
                            .delay(1, TimeUnit.SECONDS)
                            .subscribeOn(Schedulers.io())
                            .doOnSubscribe(new Consumer<Disposable>() {
                                @Override
                                public void accept(@NonNull final Disposable disposable) throws Exception {
                                    dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                                        @Override public void onCancel(DialogInterface dialog) {
                                            disposable.dispose();
                                        }
                                    });
                                    dialog.show();
                                }
                            })
                            .observeOn(AndroidSchedulers.mainThread())
                            .doOnTerminate(new Action() {
                                @Override public void run() throws Exception {
                                    dialog.dismiss();
                                }
                            })
                            .compose(RxUtil.<T>bindToLifecycle(provider));
                }
            };
        }
    
        public static <T> ObservableTransformer<T, T> applySchedulers(final LifecycleProvider provider, final ActivityEvent event, @NonNull final Dialog dialog) {
            return new ObservableTransformer<T, T>() {
                @Override public ObservableSource<T> apply(@NonNull Observable<T> upstream) {
                    return upstream
                            .delay(1, TimeUnit.SECONDS)
                            .subscribeOn(Schedulers.io())
                            .doOnSubscribe(new Consumer<Disposable>() {
                                @Override
                                public void accept(@NonNull final Disposable disposable) throws Exception {
                                    dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                                        @Override public void onCancel(DialogInterface dialog) {
                                            disposable.dispose();
                                        }
                                    });
                                    dialog.show();
                                }
                            })
                            .observeOn(AndroidSchedulers.mainThread())
                            .doOnTerminate(new Action() {
                                @Override public void run() throws Exception {
                                    dialog.dismiss();
                                }
                            })
                            .compose(RxUtil.<T>bindToLifecycle(provider, event));
                }
            };
        }
    
        public static <T> ObservableTransformer<T, T> applySchedulers(final LifecycleProvider provider, final FragmentEvent event, @NonNull final Dialog dialog) {
            return new ObservableTransformer<T, T>() {
                @Override public ObservableSource<T> apply(@NonNull Observable<T> upstream) {
                    return upstream
                            .delay(1, TimeUnit.SECONDS)
                            .subscribeOn(Schedulers.io())
                            .doOnSubscribe(new Consumer<Disposable>() {
                                @Override
                                public void accept(@NonNull final Disposable disposable) throws Exception {
                                    dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                                        @Override public void onCancel(DialogInterface dialog) {
                                            disposable.dispose();
                                        }
                                    });
                                    dialog.show();
                                }
                            })
                            .observeOn(AndroidSchedulers.mainThread())
                            .doOnTerminate(new Action() {
                                @Override public void run() throws Exception {
                                    dialog.dismiss();
                                }
                            })
                            .compose(RxUtil.<T>bindToLifecycle(provider, event));
                }
            };
        }
    }
    

    对于这个类,我自己都觉得挺蛋疼的 ,平白无故的写了很多重复的代码.主要原因在于rxlifecycle2-android这个包中的ActivityEventFragmentEvent是两个独立的枚举没法合并,为了保证传入的参数安全,只能重载方法了.

    此外这次新增了RxLifecycle中的bindUntilEvent()的支持.

    缓存

    缓存这部分,因为用了RxJava很自然的就想到去用RxCache,但是后来才发现里面有好大的一个坑,而RxCache把这个锅推给了Gson.

    如果我们的实体类中使用int类型,在正常的网络请求中能正常的获得解析,但是在配置了RxCache后,就会出现返回的对象类型发生了变化,变成了LinkedTreeMap,坑爹有木有...为啥会这样呢?

    当查看了缓存的内容后,你会发现原本的int类型变成了double了,比如用户的年龄9变成了9.0,这样原来的实体类就解析不了.只有另辟捷径了----使用okhttp3的缓存方法,Retrofit2支持给请求添加自定义的header,引用自己项目中的一个接口:

        @GET("api/car/queryCarBrand")
        @Headers("Cache-Control: public, max-age=60")
        Observable<Result<List<CarBrand>>> queryCarBrand();
    

    上面的接口添加了缓存控制,在第一次网络请求被缓存后,在60秒内不会再请求服务器而是读取缓存中的数据.

    为此我们需要再修改两个地方:
    一. Retrofit初始化的时候

    OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
    ...
    okHttpBuilder.cache(new Cache(file,size));//file是缓存路径,size缓存池大小
    ...
    

    二. 请求头的修改.

    一般情况下应该是服务器告诉我们是否该缓存这次的请求结果,虽然我们现在请求接口上配置了请求头,但是服务器不知道或者没有修改返回的请求头,我们拿到的返回结果请求头中依然没有Cache-Control,这样okhttp依然不会缓存请求结果.

    我的处理方式是在修改请求头拦截器

    public class HeaderInterceptor implements Interceptor {
        @Override public Response intercept(Chain chain) throws IOException {
            Request.Builder requestBuilder = chain.request().newBuilder();
            ArrayMap<String, String> headers = BaseApp.getAppContext().getRequestHeader();
            //如果公共请求头不为空,则构建新的请求
            if (headers != null) {
                for (String key : headers.keySet()) {
                    requestBuilder.addHeader(key, headers.get(key));
                }
            }
            Request request = requestBuilder.build();
            Response.Builder responseBuilder = chain.proceed(request).newBuilder();
            if (!TextUtils.isEmpty(request.cacheControl().toString())) {
                responseBuilder.removeHeader("Pragma").removeHeader("Cache-Control")
                        .header("Cache-Control", "public, max-age=" + request.cacheControl().maxAgeSeconds());
            }
            return responseBuilder.build();
        }
    }
    

    根据Request中是否有Cache-Control来手动修改Response,这样就做了是否缓存每次的请求结果缓存多久完全由我们自己来控制了.

    然后在第一步中增加或修改
    okHttpBuilder.addNetworkInterceptor(new HeaderInterceptor());

    这样就能正常缓存请求了.

    后续再有更新或优化,会再次更新文章~

    相关文章

      网友评论

      • FredWhite:绝对的干货啊!被这个问题整了4天!!今天才发现是由于使用了 RxCache 的原因!太TM坑爹了!楼主的那句话说得太好了!RxCache 把这个锅推给了 Gson!害得我还特地跑去换成了 FastJson,结果照样是 LinkedTreeMap。
        废话不多说,先试试楼主的方案!先行谢过了!
        FredWhite:@Android_惜年 3Q
        Android_惜年:@小丙_ad16 呵呵,共同进步,分享给你一个完整的libray,用kotlin写的 代码比较全 https://github.com/timordu/Rely-kotlin
      • 0f1d692653ae:LifecycleProvider 这个参数我在activity调用这个方法的时候应该传什么呀?activity已经继承了RxAppCompatActivity, 可以就就使用来个例子吗?
        0f1d692653ae:@Android_惜年 作者为什么不直接在applySchedulers这个方法里面直接创建 一个局部的对象loadingDialog,这样就用不着每次都要传一个loadingDialog了呀
        0f1d692653ae:@Android_惜年 恩呢,谢谢了。。。
        Android_惜年:@不败顽童_苗 直接传this, 如果是在匿名内部类里面比如onClickListener 里面 就传类.this
        RetrofitManager.getInstance().create(IApi.class)
        .login(etAccount.getText().toString(), etPassword.getText().toString())
        .compose(RxUtil.<Result<Account>>applySchedulers(this, loadingDialog))
        .subscribe(new ResultObserver<Account>() {
        @Override public void handlerResult(Account account) {
        setSharedPreferences("login_user", etAccount.getText().toString());

        ShareData.getInstance().login(mContext, account);
        skipToActivityAndFinish(HomeActivity.class, null);
        }
        });
        上面是我登录的一次的网络请求,最近在学习kotlin 正在把框架切换过去,到时候分享给大家
      • Android_惜年:我把我的library的代码和build.gradle代码提交到了github上,后期会给上完整的项目..https://github.com/timordu/Rely
        Android_惜年:@就是悟空啦 抱歉最近公司比较忙 很久没来看简书了, 没法给出具体的demo,我也还在项目中验证.https://github.com/timordu/Rely 这里是源代码
        就是悟空啦:求写一个demo

      本文标题:RxJava2+Retrofit2实现网络请求封装(三)

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