<转>RxJava+Retrofit+OkHttp深入浅出-终极封装六特殊篇(变种String替换Gson自由扩展)

背景

在之前的封装1-5Rxjava+ReTrofit+okHttp深入浅出-终极封装中我们都是通过传统的GsonConverterFactory自动解析,这样做确实很方便,用户能直接获取返回的对象,不用关心具体的转换,但是:这随之而来有很多的缺陷(虽然官网推荐这样使用);

比如:无法使用其他第三发转换框架;泛型无法中间传递,封装无法统一处理缓存结果;回调信息无法统一处理;服务器返回格式不严谨null解析异常……….
所以我们在享受它遍历的同时也被迫的要限制做很多的处理,限制我们的扩展!

本章就介绍如何放弃GsonConverterFactory,直接返回String,扩展我们的封装!(封装的整体思想和之前的封装一样,所以不会有大的改动!)

无须担心,本篇封装单独作为一个项目和之前封装分开,便于大家选择!


效果

1477899421_7354.gif

功能

完全具备和之前封装一样的功能,这里改用fastjson处理

    1.Retrofit+Rxjava+okhttp基本使用方法
    2.统一处理请求数据格式
    3.统一的ProgressDialog和回调Subscriber处理
    4.取消http请求
    5.预处理http请求
    6.返回数据的统一判断
    7.失败后的retry处理
    8.RxLifecycle管理生命周期,防止泄露
    9.文件上传下载(支持多文件,断点续传)
    10.Cache数据持久化和数据库(greenDao)两种缓存机制
    11.一对多回调接口处理

对比

话说没有比较就没有进步,所以大家比较下前后封装的各自的优缺点,自行选择合适自己的方案!

使用

Gson方案:
    //    完美封装简化版
    private void simpleDo() {
        SubjectPostApi postEntity = new SubjectPostApi(simpleOnNextListener,this);
        postEntity.setAll(true);
        HttpManager manager = HttpManager.getInstance();
        manager.doHttpDeal(postEntity);
    }

    //   回调一一对应
    HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<SubjectResulte>>() {
        @Override
        public void onNext(List<SubjectResulte> subjects) {
            tvMsg.setText("网络返回:\n" + subjects.toString());
        }

        @Override
        public void onCacheNext(String cache) {
            /*缓存回调*/
            Gson gson=new Gson();
            java.lang.reflect.Type type = new TypeToken<BaseResultEntity<List<SubjectResulte>>>() {}.getType();
            BaseResultEntity resultEntity= gson.fromJson(cache, type);
            tvMsg.setText("缓存返回:\n"+resultEntity.getData().toString() );
        }

        /*用户主动调用,默认是不需要覆写该方法*/
        @Override
        public void onError(Throwable e) {
            super.onError(e);
            tvMsg.setText("失败:\n" + e.toString());
        }

        /*用户主动调用,默认是不需要覆写该方法*/
        @Override
        public void onCancel() {
            super.onCancel();
            tvMsg.setText("取消請求");
        }
    };
String方案

    //    完美封装简化版
    private void simpleDo() {
         /*初始化数据*/
        manager=new HttpManager(this,this);
        postEntity = new SubjectPostApi();
        postEntity.setAll(true);
        manager.doHttpDeal(postEntity);
    }



    @Override
    public void onNext(String resulte, String mothead) {
        /*post返回处理*/
        if(mothead.equals(postEntity.getMothed())){
            List<SubjectResulte>  subjectResulte= JSONObject.parseArray(resulte,SubjectResulte.class);
            tvMsg.setText("post返回:\n"+subjectResulte.toString() );
        }

        /*上传返回处理*/
        if(mothead.equals(uplaodApi.getMothed())){
            UploadResulte uploadResulte=JSONObject.parseObject(resulte,UploadResulte.class);
            tvMsg.setText("上传成功返回:\n"+uploadResulte.getHeadImgUrl());
            Glide.with(MainActivity.this).load(uploadResulte.getHeadImgUrl()).skipMemoryCache(true).into(img);
        }
    }

    @Override
    public void onError(Throwable e) {
        tvMsg.setText("失败:\n" + e.toString());
    }

Gson封装方案中,我们采用了一一对应的返回原则,将所以的请求数据参数都放入到baseApi中,返回放入对应的HttpOnNextListener 中
String方案中我们则采用一对多原则,将回调和请求分开处理,公用一个回调,通过回调中的mothead来区分不同的接口,所以上述可以看见后者里面其实还处理了上传的回调处理!

从封装的用法上可以看出:
优点:String封装更加的灵活,可以指定Gson转换的第三方工具,统一的结果返回处理代码更加的少(可以完美解决缓存无法统一回调的问题);
同样也有缺点:String封装无法自动解析结果类型,需要手动处理(我反而觉得这也是它的优点,更加的灵活,个人看法)


实现

由于是基于之前的封装修改,所以前提是了解之前的封装以后才能完全了解一下的修改实现思路RxJava+Retrofit+OkHttp深入浅出-终极封装专栏

1.替换GsonConverterFactory

由于GsonConverterFactory会自动解析Gson,替换成直接返回String的ScalarsConverterFactory
导入相关包(为了区别-使用fastjson可自由扩展)

    compile 'com.squareup.retrofit2:converter-scalars:+'
    compile 'com.alibaba:fastjson:+'

替换

 compile 'com.squareup.retrofit2:converter-gson:+'
 compile 'com.google.code.gson:gson:+'
2.修改retrofit构建

ScalarsConverterFactory替换GsonConverterFactory

  /*创建retrofit对象*/
  Retrofit retrofit = new Retrofit.Builder()
            .client(builder.build())
            .addConverterFactory(ScalarsConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .baseUrl(basePar.getBaseUrl())
            .build();
  HttpService  httpService = retrofit.create(HttpService.class);

替换

  /*创建retrofit对象*/
  Retrofit retrofit = new Retrofit.Builder()
            .client(builder.build())
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .baseUrl(basePar.getBaseUrl())
            .build();
  HttpService  httpService = retrofit.create(HttpService.class);
3.修改缓存记录位置

由于之前是为了防止gson重复解析,将缓存放入到自定义CookieInterceptor中;既然现在不需要自动转换,直接返回String,所以直接将缓存数据处理放入到ProgressSubscriber的onNext中处理;

修改1:去掉CookieInterceptor
//手动创建一个OkHttpClient并设置超时时间缓存等设置
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addInterceptor(new CacheInterceptor());
builder.addNetworkInterceptor(new CacheInterceptor());

替换

//手动创建一个OkHttpClient并设置超时时间缓存等设置
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addNetworkInterceptor(new CacheInterceptor());
builder.addInterceptor(new CookieInterceptor(basePar.isCache()));
修改2:实现缓存处理

在onNext中实现缓存处理

    /**
     * 将onNext方法中的返回结果交给Activity或Fragment自己处理
     *
     * @param t 创建Subscriber时的泛型类型
     */
    @Override
    public void onNext(T t) {
         /*缓存处理*/
        if(api.isCache()){
            CookieResulte resulte= CookieDbUtil.getInstance().queryCookieBy(api.getUrl());
            long time=System.currentTimeMillis();
            /*保存和更新本地数据*/
            if(resulte==null){
                resulte  =new CookieResulte(api.getUrl(),t.toString(),time);
                CookieDbUtil.getInstance().saveCookie(resulte);
            }else{
                resulte.setResulte(t.toString());
                resulte.setTime(time);
                CookieDbUtil.getInstance().updateCookie(resulte);
            }
        }
        if (mSubscriberOnNextListener.get() != null) {
            mSubscriberOnNextListener.get().onNext((String) t,api.getMothed());
        }
    }
4.修改回调接口信息

由于现在通过String直接返回,所以可以将成功回调和缓存回调合并处理;另一方面没有了泛型的限制,在回调时可以通过接口请求参数实现一对多回调处理;

/**
 * 成功回调处理
 * Created by WZG on 2016/7/16.
 */
public interface  HttpOnNextListener {
    /**
     * 成功后回调方法
     * @param resulte
     * @param method
     */
   void onNext(String resulte,String method);

    /**
     * 失败或者错误方法
     * 主动调用,更加灵活
     * @param e
     */
   void onError(Throwable e);
}
5.修改BaseApi

由于取消了泛型返回的机制,所以在Func1判断时需要手动转换数据;这里示例fastjeson用法转换

 @Override
    public String call(T httpResult) {
        BaseResultEntity baseResulte= JSONObject.parseObject(httpResult.toString(),BaseResultEntity.class);
        if (baseResulte.getRet() == 0) {
            throw new HttpTimeException(baseResulte.getMsg());
        }
        return baseResulte.getData();
    }

替换

    @Override
    public T call(BaseResultEntity<T> httpResult) {
        if (httpResult.getRet() == 0) {
            throw new HttpTimeException(httpResult.getMsg());
        }
        return httpResult.getData();
    }
6.修改结果基础类BaseResultEntity

将泛型数据改成String数据类型

/**
 * 回调信息统一封装类
 * Created by WZG on 2016/7/16.
 */
public class BaseResultEntity {
    //  判断标示
    private int ret;
    //    提示信息
    private String msg;
    //显示数据(用户需要关心的数据)
    private String data;
 }

替换

/**
 * 回调信息统一封装类
 * Created by WZG on 2016/7/16.
 */
public class BaseResultEntity<T> {
    //  判断标示
    private int ret;
    //    提示信息
    private String msg;
    //显示数据(用户需要关心的数据)
    private T data;
 }
7.合并缓存和成功回到返回处理

由于取消泛型,缓存和成统一处理所以需要修改


    /**
     * 订阅开始时调用
     * 显示ProgressDialog
     */
    @Override
    public void onStart() {
        showProgressDialog();
        /*缓存并且有网*/
        if(api.isCache()&& AppUtil.isNetworkAvailable(MyApplication.app)){
             /*获取缓存数据*/
            CookieResulte cookieResulte= CookieDbUtil.getInstance().queryCookieBy(api.getUrl());
            if(cookieResulte!=null){
                long time= (System.currentTimeMillis()-cookieResulte.getTime())/1000;
                if(time< api.getCookieNetWorkTime()){
                    if( mSubscriberOnNextListener.get()!=null){
                        mSubscriberOnNextListener.get().onNext(cookieResulte.getResulte(),api.getMothed());
                    }
                    onCompleted();
                    unsubscribe();
                }
            }
        }
    }

替换

    /**
     * 订阅开始时调用
     * 显示ProgressDialog
     */
    @Override
    public void onStart() {
        showProgressDialog();
        /*缓存并且有网*/
        if(api.isCache()&& AppUtil.isNetworkAvailable(MyApplication.app)){
             /*获取缓存数据*/
            CookieResulte cookieResulte= CookieDbUtil.getInstance().queryCookieBy(api.getUrl());
            if(cookieResulte!=null){
                long time= (System.currentTimeMillis()-cookieResulte.getTime())/1000;
                if(time< api.getCookieNetWorkTime()){
                    if( mSubscriberOnNextListener.get()!=null){
                        mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
                    }
                    onCompleted();
                    unsubscribe();
                }
            }
        }
    }
8.修改一对多回调处理

没有了泛型,可以修改HttpManager,采用动态创建,动态回调的方法解决多嵌套耦合的问题

修改1:去掉默认构造传参
public BaseApi(HttpOnNextListener listener, RxAppCompatActivity rxAppCompatActivity) {
        setListener(listener);
        setRxAppCompatActivity(rxAppCompatActivity);
        setShowProgress(true);
        setCache(true);
    }
修改2:添加HttpManager动态传参
/**
 * http交互处理类
 * Created by WZG on 2016/7/16.
 */
public class HttpManager {
    /*弱引用對象*/
    private SoftReference<HttpOnNextListener>  onNextListener;
    private SoftReference<RxAppCompatActivity> appCompatActivity;

    public HttpManager(HttpOnNextListener onNextListener, RxAppCompatActivity appCompatActivity) {
        this.onNextListener=new SoftReference(onNextListener);
        this.appCompatActivity=new SoftReference(appCompatActivity);
    }
    *******************
    *******************
}
修改3:通过method动态判断接口返回
public class MainActivity extends RxAppCompatActivity implements HttpOnNextListener{
    @Override
    public void onNext(String resulte, String method) {
        /*post返回处理*/
        if(method.equals(postEntity.getMothed())){
           *******
        }

        /*上传返回处理*/
        if(method.equals(uplaodApi.getMothed())){
           *********
        }
    }

    @Override
    public void onError(Throwable e) {
        tvMsg.setText("失败:\n" + e.toString());
    }
 }

大功告成!


下载模块

由于下载模块是独立存在,所以基本没有修改,唯一修改的地方就是将HttpDownManager中的GsonConverterFactory替换成ScalarsConverterFactory即可!

总结

通过自定义String类型的返回处理方式,有效的解决了之前Gson自动转换的问题

  • 1.一对一返回问题(代码量多)

  • 2.缓存回调无法和成功统一处理

  • 3.无法指定gson转换第三方库

  • 4.回调监听的多嵌套(耦合度大)

  • 5.解决服务器数据null异常

注意:这里只是给大家提供了一个不同的解决方案,Gson自动解析返回的方案也是有它的优点,可以大大的减少开发的工作量,优缺点也很明显;孰好孰坏自行判断,自行选择适合自己的方案(个人偏向后者String返回,比较灵活)

源码

源码传送门-String变种方案-Github
源码传送门-Gson方案-Github

终极封装专栏

RxJava+Retrofit+OkHttp深入浅出-终极封装专栏

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

推荐阅读更多精彩内容