封装Retrofit2+RxJava2网络请求框架

2021年1月18号补充
最近遇到一个5.0以下的设备无法请求问题,主要的原因都是Okhttp4不支持5.0以下的设备,这边我们需要做下兼容,不过我的设想是通过Gradle去做兼容,暂时还没有弄出来。可以看下这个博客:
https://blog.csdn.net/mr_tony/article/details/108776208
2021年8月19号

这个博客写了很久了,陆续还有人在点赞,我还是比较高兴的,个人觉得ResponseTransformer那一块代码可能有点晦涩难懂,有些读者也问过我这个变化的问题,如果参考我的代码觉得可以优化掉那一块代码我觉得也行,毕竟代码写得简洁和可维护最好。ResposeTransformer那边实际上用的是Rxjava的一种变换,主要是我好久不用Rxjava了,突然看了下这块代码,自己也有点懵逼,不过代码是可以跑通的,而且也解释了这个变换,我觉得如果Rxjava懂的人应该能看懂。谢谢大家支持。

因为Retrofit方便使用和支持RxJava,所以Retrofit已经成为了非常流行的网络框架。学会封装和使用Retrofit网络请求框架练练手是提升自己架构水平一个非常好的示例。而且当成功封装第一个组件时,再次遇到需要封装组件这样的任务也会变得得心应手。

1、封装的主要逻辑

主要的逻辑

根据这个逻辑图一步一步封装网络框架吧。

1.1 导入依赖
    compile "io.reactivex.rxjava2:rxjava:2.1.0" // 必要rxjava2依赖
    compile "io.reactivex.rxjava2:rxandroid:2.0.1" // 必要rxandrroid依赖,切线程时需要用到
    compile 'com.squareup.retrofit2:retrofit:2.3.0' // 必要retrofit依赖
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' // 必要依赖,和Rxjava结合必须用到,下面会提到
    compile 'com.squareup.retrofit2:converter-gson:2.3.0' // 必要依赖,解析json字符所用
    compile 'com.squareup.okhttp3:logging-interceptor:3.8.1' //非必要依赖, log依赖,如果需要打印OkHttpLog需要添加
1.2 新建Manger类,管理所需要的API

这里是一个千篇一律的套路,如果封装全局使用的框架,并且需要和整个软件有一样长的生命周期。那就有两个特点:

  • 在Application里面初始化。
  • 使用单例模式。
  • 在一个"管理类"中初始化所需要的所有参数。
/**
 * Created by Zaifeng on 2018/2/28.
 * API初始化类
 */
public class NetWorkManager {

    private static NetWorkManager mInstance;
    private static Retrofit retrofit;
    private static volatile Request request = null;

    public static NetWorkManager getInstance() {
        if (mInstance == null) {
            synchronized (NetWorkManager.class) {
                if (mInstance == null) {
                    mInstance = new NetWorkManager();
                }
            }
        }
        return mInstance;
    }

    /**
     * 初始化必要对象和参数
     */
    public void init() {
        // 初始化okhttp
        OkHttpClient client = new OkHttpClient.Builder()
                .build();

        // 初始化Retrofit
        retrofit = new Retrofit.Builder()
                .client(client)
                .baseUrl(Request.HOST)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }

    public static Request getRequest() {
        if (request == null) {
            synchronized (Request.class) {
                request = retrofit.create(Request.class);
            }
        }
        return request;
    }
}

这里新建了NetWorkManager 在init方法中初始化了OkHttp和Retrofit。两者的初始化都是构造者模式。

初始化OKHttp的拓展
上面的代码只是添加了必要的属性,还可以对OkHttp和Retrofit添加更多的拓展。比如:需要对OkHttp添加Log可以如下写。

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(logging)
                    .build();

addInterceptor()还有很多的用法,比如:封装一些公共的参数等等。参考如下博客:http://blog.csdn.net/jdsjlzx/article/details/52063950
这里就不多说明。

初始化Retrofit的时候的两个必要配置:

  • 第一个配置:.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    这个是用来决定你的返回值是Observable还是Call。
    // 使用call的情况
    Call<String> login();  

    // 使用Observable的情况
    Observable<String> login();  

如果返回为Call那么可以不添加这个配置。如果使用Observable那就必须添加这个配置。否则就会请求的时候就会报错!
对于这个配置,详细可以看下这篇博客。
http://blog.csdn.net/new_abc/article/details/53021387

  • 第二个配置:.addConverterFactory(GsonConverterFactory.create())
    这个配置是将服务器返回的json字符串转化为对象。这个是可以自定义Converter来应对服务器返回的不同的数据的。具体如何去自定义,这里也不做说明了(因为内容比较多),可以看下如下的博客。
    https://www.jianshu.com/p/5b8b1062866b

初始化Request
这里也初始化了Request。如果有Retrofit基础看到retrofit.create这个方法,就应该知道Request是定义的请求的服务器的API封装类。里面通过注解的方式声明所需要请求的接口,这里下面会讲到。

1.3 约定Response

在解析服务器返回的数据时,需要和服务器统一返回Json格式,这个在开发中经常遇到。一般服务器也不会随便返回Json格式给前端,否则前端解析时将会非常麻烦。(所以这里的数据结构需要前端和服务端约定一个固定的格式)
这里的固定json格式是:

{ret:0,data:"",msg:""}

所以这里定义的固定的Response为:

public class Response<T> {

    private int ret; // 返回的code
    private T data; // 具体的数据结果
    private String msg; // message 可用来返回接口的说明

    public int getCode() {
        return ret;
    }

    public void setCode(int code) {
        this.ret = code;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}
1.4 定义Request

在前面NetWorkManager中已经提到了,这个是定义的请求服务器的API的接口。

public interface Request {

    // 填上需要访问的服务器地址
    public static String HOST = "https://www.xxx.com/app_v5/";
    
    @POST("?service=sser.getList")
    Observable<Response<List<javaBean>>> getList(@Query("id") String id);
}

这里用@Post注解了一个Post请求。

1.5 定义ResponseTransformer处理数据和异常

这个也是自己定义的类,不使用这个自定义的类也是可以的。但是为了封装之后使用的便捷性,还是建议封装。ResponseTransformer实际上是Rxjava中的“变换”的封装。
没使用ResponseTransformer时的情况。

 model.getCarList("xxxxx")
                .compose(schedulerProvider.applySchedulers())
                .subscribe(new Consumer<Response<List<JavaBean>>>() {
                    @Override
                    public void accept(Response<List<JavaBean>> listResponse) throws Exception {
                        if(listResponse.getCode() == 200){
                            List<JavaBean> javaBeans = listResponse.getData();
                        }else{
                            // 异常处理
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                            // 异常处理
                    }
                });

使用了ResponseTransformer的情况:

 model.getCarList("xxxxx")
                .compose(ResponseTransformer.handleResult())
                .compose(schedulerProvider.applySchedulers())
                .subscribe(new Consumer<List<JavaBean>>() {
                               @Override
                               public void accept(List<JavaBean> carBeans) throws Exception {
                                   // 处理数据 直接获取到List<JavaBean> carBeans
                               }
                           }, new Consumer<Throwable>() {
                               @Override
                               public void accept(Throwable throwable) throws Exception {
                                   // 处理异常
                               }
                           }

                );

没有使用ResponseTransformer,需要先判断Response的code,然后将数据从Response中取出来。比较繁琐的就是判断code==200的代码,每次请求接口都需要去写一遍。
使用了ResponseTransformer将解析的Response数据进一步处理(将判断code==200的代码进行封装),直接将需要使用的数据处理提取出来,并且将Exception统一处理!这个就是RxJava中“变换”的妙用,同时在使用时会和“compose”联用,减少重复代码。
具体的ResponseTransformer代码。

public class ResponseTransformer {

    public static <T> ObservableTransformer<Response<T>, T> handleResult() {
        return upstream -> upstream
                .onErrorResumeNext(new ErrorResumeFunction<>())
                .flatMap(new ResponseFunction<>());
    }


    /**
     * 非服务器产生的异常,比如本地无无网络请求,Json数据解析错误等等。
     *
     * @param <T>
     */
    private static class ErrorResumeFunction<T> implements Function<Throwable, ObservableSource<? extends Response<T>>> {

        @Override
        public ObservableSource<? extends Response<T>> apply(Throwable throwable) throws Exception {
            return Observable.error(CustomException.handleException(throwable));
        }
    }

    /**
     * 服务其返回的数据解析
     * 正常服务器返回数据和服务器可能返回的exception
     *
     * @param <T>
     */
    private static class ResponseFunction<T> implements Function<Response<T>, ObservableSource<T>> {

        @Override
        public ObservableSource<T> apply(Response<T> tResponse) throws Exception {
            int code = tResponse.getCode();
            String message = tResponse.getMsg();
            if (code == 200) {
                return Observable.just(tResponse.getData());
            } else {
                return Observable.error(new ApiException(code, message));
            }
        }
    }
}

1.6、 处理Exception

这里的Exception分为两部分:

  • 自己本地产生的Exception,比如解析错误,网络链接错误等等。
  • 服务器产生的Excption,比如404,503等等服务器返回的Excption。

定义ApiException统一处理:

public class ApiException extends Exception {
    private int code;
    private String displayMessage;

    public ApiException(int code, String displayMessage) {
        this.code = code;
        this.displayMessage = displayMessage;
    }

    public ApiException(int code, String message, String displayMessage) {
        super(message);
        this.code = code;
        this.displayMessage = displayMessage;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getDisplayMessage() {
        return displayMessage;
    }

    public void setDisplayMessage(String displayMessage) {
        this.displayMessage = displayMessage;
    }
}

对于本地产生的Exception如下处理:

public class CustomException {

    /**
     * 未知错误
     */
    public static final int UNKNOWN = 1000;

    /**
     * 解析错误
     */
    public static final int PARSE_ERROR = 1001;

    /**
     * 网络错误
     */
    public static final int NETWORK_ERROR = 1002;

    /**
     * 协议错误
     */
    public static final int HTTP_ERROR = 1003;

    public static ApiException handleException(Throwable e) {
        ApiException ex;
        if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException) {
            //解析错误
            ex = new ApiException(PARSE_ERROR, e.getMessage());
            return ex;
        } else if (e instanceof ConnectException) {
            //网络错误
            ex = new ApiException(NETWORK_ERROR, e.getMessage());
            return ex;
        } else if (e instanceof UnknownHostException || e instanceof SocketTimeoutException) {
            //连接错误
            ex = new ApiException(NETWORK_ERROR, e.getMessage());
            return ex;
        } else {
            //未知错误
            ex = new ApiException(UNKNOWN, e.getMessage());
            return ex;
        }
    }
}

这些异常如何抛出呢 ? 那就看下上面提到的 ResponseTransformer 中处理异常的代码吧。两种异常都通过Observable.error发射出去。

// 本地异常处理
Observable.error(CustomException.handleException(throwable));

// 对服务器放回的code进行判断
 int code = tResponse.getCode();
 String message = tResponse.getMsg();
 if (code == 200) {       
     return Observable.just(tResponse.getData());
 } else {
     return Observable.error(new ApiException(code, message));
  }
1.7、线程切换

使用过Rxjava对Rxjava线程切换应该比较熟悉,这里将线程切换单独封装成一个类再结合compose使用。

public class SchedulerProvider implements BaseSchedulerProvider {

    @Nullable
    private static SchedulerProvider INSTANCE;

    // Prevent direct instantiation.
    private SchedulerProvider() {
    }

    public static synchronized SchedulerProvider getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new SchedulerProvider();
        }
        return INSTANCE;
    }

    @Override
    @NonNull
    public Scheduler computation() {
        return Schedulers.computation();
    }

    @Override
    @NonNull
    public Scheduler io() {
        return Schedulers.io();
    }

    @Override
    @NonNull
    public Scheduler ui() {
        return AndroidSchedulers.mainThread();
    }

    @NonNull
    @Override
    public <T> ObservableTransformer<T, T> applySchedulers() {
        return observable -> observable.subscribeOn(io())
                .observeOn(ui());
    }
}

2、实战

封装好之后,接下来就是实际使用了。不过在实际开发中可能是先实现逻辑再去封装,很多东西需要在代码写完之后才知道要去封装起来的。

2.1、初始化

BaseApplication中初始化。

public class BaseApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        NetWorkManager.getInstance().init();
    }
}
2.2、搭建好MVP结构,在Presenter中使用。
    Disposable disposable = model.getCarList("xxxxxx")
                .compose(ResponseTransformer.handleResult())
                .compose(schedulerProvider.applySchedulers())
                .subscribe(carBeans -> {
                    // 处理数据 直接获取到List<JavaBean> carBeans
                    view.getDataSuccess();
                }, throwable -> {
                    // 处理异常
                    view.getDataFail();
                });

        mDisposable.add(disposable);

两个回调,回调之后View层做出相应的UI变化即可。
这里用了compose的使用就不多做解释。这里compose结合线程的切换封装和变换封装,减少了冗余代码。
关于compose的链接:https://www.jianshu.com/p/3d0bd54834b0
最后的目录结构:

目录结构

3、需要注意的地方

1、很多时候需要实现效果才能再去进行封装。
2、在封装框架的时候封装的是逻辑层,不要在逻辑层中写业务层的代码。

源码地址:https://github.com/AxeChen/retrofit2_rxjava2

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

推荐阅读更多精彩内容