您的位置:首页 > 理论基础 > 计算机网络

Rxjava+ReTrofit+okHttp深入浅出-终极封装

2016-12-22 13:30 465 查看
背景:

学习Rxjava和retrofit已经很长时间了,功能确实很强大,但是使用起来还是有点复杂,代码的重复性太高,所以决定把基于retrofit和rxjava的处理统一封装起来,实现的功能:

1.Retrofit+Rxjava+okhttp基本使用方法

2.统一处理请求数据格式

3.统一的ProgressDialog和回调Subscriber处理

4.取消http请求

5.预处理http请求

6.返回数据的统一判断

7.失败后的retry封装处理

8.RxLifecycle管理生命周期,防止泄露

9.文件上传和文件下载(支持多文件断点续传)

10.数据持久化

博客发出后谢谢大家的关注,原本只是自己学习后的一些经验总结,但是有同学运用到实战当中,这让我很惶恐(害怕出现问题会影响大家的项目开发);所以决定长期维护这个开源的封装,如果在使用过程中有任何问题,欢迎大家反馈!我这样也会及时更新和维护记录,方便大家及时查看和更新!所以博客部分地方会和最新版本有出入,但是整体思想是完全一样的,大家任然可以阅读本篇博客!后期也会出一个专栏,讲述下封装思想,欢迎大家关注!

效果:

封装后http请求代码如下

[java] view plain copy

在CODE上查看代码片派生到我的代码片

// 完美封装简化版

private void simpleDo() {

SubjectPost postEntity = new SubjectPost(simpleOnNextListener,this);

postEntity.setAll(true);

HttpManager manager = HttpManager.getInstance();

manager.doHttpDeal(postEntity);

}

// 回调一一对应

HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<Subject>>() {

@Override

public void onNext(List<Subject> subjects) {

tvMsg.setText("已封装:\n" + subjects.toString());

}

/*用户主动调用,默认是不需要覆写该方法*/

@Override

public void onError(Throwable e) {

super.onError(e);

tvMsg.setText("失败:\n" + e.toString());

}

};

是不是很简单?你可能说这还简单,好咱们对比一下正常使用retrofit的方法

[html] view plain copy

在CODE上查看代码片派生到我的代码片

/**

* Retrofit加入rxjava实现http请求

*/

private void onButton9Click() {

//手动创建一个OkHttpClient并设置超时时间

okhttp3.OkHttpClient.Builder builder = new OkHttpClient.Builder();

builder.connectTimeout(5, TimeUnit.SECONDS);

Retrofit retrofit = new Retrofit.Builder()

.client(builder.build())

.addConverterFactory(GsonConverterFactory.create())

.addCallAdapterFactory(RxJavaCallAdapterFactory.create())

.baseUrl(HttpManager.BASE_URL)

.build();

/ 加载框

final ProgressDialog pd = new ProgressDialog(this);

HttpService apiService = retrofit.create(HttpService.class);

Observable<RetrofitEntity> observable = apiService.getAllVedioBy(true);

observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())

.subscribe(

new Subscriber<RetrofitEntity>() {

@Override

public void onCompleted() {

if (pd != null && pd.isShowing()) {

pd.dismiss();

}

}

@Override

public void onError(Throwable e) {

if (pd != null && pd.isShowing()) {

pd.dismiss();

}

}

@Override

public void onNext(RetrofitEntity retrofitEntity) {

tvMsg.setText("无封装:\n" + retrofitEntity.getData().toString());

}

@Override

public void onStart() {

super.onStart();

pd.show();

}

}

);

}

可能你发现确是代码有点多,但是更加可怕的是,如果你一个activity或者fragment中多次需要http请求,你需要多次重复的写回调处理(一个回到就有4个方法呀!!!!反正我是忍受不了),而且以上处理还没有做过多的判断和错误校验就如此复杂!~好了介绍完了,开始咱们的优化之路吧!

项目结构:

Rxjava

如果你对Rxjava不了解,好吧骚年赶快学学吧,不然真会out了*******扔物线的金典rxjava

进阶:

Rxjava进阶一

Rxjava进阶二

Rxjava进阶三

Rxjava进阶四

ReTrofit基本设置

咱家今天的主角来了,咱们也深入浅出一下,前方高能,如果你是深度retrofit选手请直接跳过本节!!!

1.首先确保在AndroidManifest.xml中请求了网络权限

[html] view plain copy

在CODE上查看代码片派生到我的代码片

<uses-permission android:name="android.permission.INTERNET"/>

2.在app/build.gradle添加引用

[html] view plain copy

在CODE上查看代码片派生到我的代码片

/*rx-android-java*/

compile 'io.reactivex:rxjava:+'

compile 'com.squareup.retrofit:adapter-rxjava:+'

compile 'com.trello:rxlifecycle:+'

compile 'com.trello:rxlifecycle-components:+'

/*rotrofit*/

compile 'com.squareup.retrofit2:retrofit:+'

compile 'com.squareup.retrofit2:converter-gson:+'

compile 'com.squareup.retrofit2:adapter-rxjava:+'

compile 'com.google.code.gson:gson:+'

ReTrofit基本使用:

后面的文章中我们都是用这个接口调试

[html] view plain copy

在CODE上查看代码片派生到我的代码片

/**

* @api videoLink 50音图视频链接

* @url http://www.izaodao.com/Api/AppFiftyToneGraph/videoLink
* @method post

* @param once_no bool(选填,ture无链接) 一次性获取下载地址

* @return json array(

* ret:1成功,2失败

* msg:信息

* data:{

* name:视频名称

* title:标题

* }

)

1.初始化retrofit

要向一个api发送我们的网络请求 ,我们需要使用 Retrofit builder 类并指定service的base URL (通常情况下就是域名)。

[html] view plain copy

在CODE上查看代码片派生到我的代码片

String BASE_URL = " http://www.izaodao.com/Api/"
Retrofit retrofit = new Retrofit.Builder()

.baseUrl(BASE_URL)

.a
cd16
ddConverterFactory(GsonConverterFactory.create())

.build();

2.设置接口service

注意到每个endpoint 都指定了一个关于HTTP(GET, POST, 等等。) 方法的注解以及用于分发网络调用的方法。而且这些方法的参数也可以有特殊的注解。

[html] view plain copy

在CODE上查看代码片派生到我的代码片

/**

* 接口地址

* Created by WZG on 2016/7/16.

*/

public interface MyApiEndpointInterface {

@POST("AppFiftyToneGraph/videoLink")

Call<RetrofitEntity> getAllVedio(@Body boolean once_no);

}

3.得到call然后同步处理处理回调:

[html] view plain copy

在CODE上查看代码片派生到我的代码片

MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class);

Call<RetrofitEntity> call = apiService.getAllVedio(true);

call.enqueue(new Callback<RetrofitEntity>() {

@Override

public void onResponse(Response<RetrofitEntity> response, Retrofit retrofit) {

RetrofitEntity entity = response.body();

Log.i("tag", "onResponse----->" + entity.getMsg());

}

@Override

public void onFailure(Throwable t) {

Log.i("tag", "onFailure----->" + t.toString());

}

});

ReTrofit+Rxjava基本使用

和之前的retrofit使用区别:

1.在于我们需要修改service接口返回信息我们需要返回一个Observable对象

[html] view plain copy

在CODE上查看代码片派生到我的代码片

@POST("AppFiftyToneGraph/videoLink")

Observable<RetrofitEntity> getAllVedioBy(@Body boolean once_no);

2.然后初始化Retrofit需要添加对Rxjava的适配,注意一定要retrofit2才有这个功能哦

[html] view plain copy

在CODE上查看代码片派生到我的代码片

Retrofit retrofit = new Retrofit.Builder()

.client(builder.build())

.addConverterFactory(GsonConverterFactory.create())

.addCallAdapterFactory(RxJavaCallAdapterFactory.create())

.baseUrl(HttpManager.BASE_URL)

.build();

3.回调交个rxjava去处理

[html] view plain copy

在CODE上查看代码片派生到我的代码片

HttpService apiService = retrofit.create(HttpService.class);

Observable<RetrofitEntity> observable = apiService.getAllVedioBy(true);

observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())

.subscribe(

new Subscriber<RetrofitEntity>() {

@Override

public void onCompleted() {

}

@Override

public void onError(Throwable e) {

}

@Override

public void onNext(RetrofitEntity retrofitEntity) {

tvMsg.setText("无封装:\n" + retrofitEntity.getData().toString());

}

}

);

ReTrofit+Rxjava进阶封装之路

1.请求操作类封装,统一处理过程:

1.1首先初始化一个单利方便http请求;这里用了volatile的对象,不懂的同学可以参考我的另一篇博客

[html] view plain copy

在CODE上查看代码片派生到我的代码片

private volatile static HttpManager INSTANCE;

//构造方法私有

private HttpManager() {

//手动创建一个OkHttpClient并设置超时时间

OkHttpClient.Builder builder = new OkHttpClient.Builder();

builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);

Retrofit retrofit = new Retrofit.Builder()

.client(builder.build())

.addConverterFactory(GsonConverterFactory.create())

.addCallAdapterFactory(RxJavaCallAdapterFactory.create())

.baseUrl(BASE_URL)

.build();

httpService = retrofit.create(HttpService.class);

}

//获取单例

public static HttpManager getInstance() {

if (INSTANCE == null) {

synchronized (HttpManager.class) {

if (INSTANCE == null) {

INSTANCE = new HttpManager();

}

}

}

return INSTANCE;

}

1.2接口处理和回调处理:

basePar.getObservable主要是得打service中定义的observable对象(我们需要处理的接口对象)RxMap方法是统一处理结果回调信息处理basePar.getSubscirber获取需要统一结果处理的subscirber对象

[java] view plain copy

在CODE上查看代码片派生到我的代码片

/**

* 处理http请求

*

* @param basePar 封装的请求数据

*/

public void doHttpDeal(BaseEntity basePar) {

Observable observable = basePar.getObservable(httpService)

/*失败后的retry配置*/

.retryWhen(new RetryWhenNetworkException())

/*生命周期管理*/

.compose(basePar.getRxAppCompatActivity().bindToLifecycle())

/*http请求线程*/

.subscribeOn(Schedulers.io())

.unsubscribeOn(Schedulers.io())

/*回调线程*/

.observeOn(AndroidSchedulers.mainThread())

/*结果判断*/

.map(basePar);

/*数据回调*/

observable.subscribe(basePar.getSubscirber());

}

2.数据统一请求参数和数据封装BaseEntity

2.1:getObservable通过传入一个**service对象获取需要处理的Observable对象

2.2:getSubScirber得到一个回调sub对象

这两个方法都需要其子类实现覆盖

2.3:在base里面我们继承Fucn1方法通过泛型去处理服务器返回数据判断(这时候我们可以在回调信息前提前处理错误信息),可以自己封装信息哦!如果是有效数据才返回给next方法,错误信息或者无数据等直接抛出一个自己定义的异常信息在onerror方法中处理!

[html] view plain copy

在CODE上查看代码片派生到我的代码片

/**

* 请求数据统一封装类

* Created by WZG on 2016/7/16.

*/

public abstract class BaseEntity<T> implements Func1<BaseResultEntity<T>, T> {

/**

* 设置参数

*

* @param methods

* @return

*/

public abstract Observable getObservable(HttpService methods);

/**

* 设置回调sub

*

* @return

*/

public abstract Subscriber getSubscirber();

public T call(BaseResultEntity<T> httpResult) {

if (httpResult.getRet() == 0) {

throw new HttpTimeException(httpResult.getMsg());

}

return httpResult.getData();

}

}

3自定义异常处理

[html] view plain copy

在CODE上查看代码片派生到我的代码片

/**

* 自定义错误信息,统一处理返回处理

* Created by WZG on 2016/7/16.

*/

public class HttpTimeException extends RuntimeException {

public static final int NO_DATA = 0x2;

public HttpTimeException(int resultCode) {

this(getApiExceptionMessage(resultCode));

}

public HttpTimeException(String detailMessage) {

super(detailMessage);

}

/**

* 转换错误数据

*

* @param code

* @return

*/

private static String getApiExceptionMessage(int code) {

String message = "";

switch (code) {

case NO_DATA:

message = "无数据";

break;

default:

message = "error";

break;

}

return message;

}

}

4.请求失败后的retry处理

[java] view plain copy

在CODE上查看代码片派生到我的代码片

public class RetryWhenNetworkException implements Func1<Observable<? extends Throwable>, Observable<?>> {

// retry次数

private int count = 5;

// 延迟

private long delay = 5000;

// 叠加延迟

private long increaseDelay = 5000;

public RetryWhenNetworkException() {

}

public RetryWhenNetworkException(int count, long delay) {

this.count = count;

this.delay = delay;

}

public RetryWhenNetworkException(int count, long delay, long increaseDelay) {

this.count = count;

this.delay = delay;

this.increaseDelay = increaseDelay;

}

@Override

public Observable<?> call(Observable<? extends Throwable> observable) {

return observable

.zipWith(Observable.range(1, count + 1), new Func2<Throwable, Integer, Wrapper>() {

@Override

public Wrapper call(Throwable throwable, Integer integer) {

return new Wrapper(throwable, integer);

}

}).flatMap(new Func1<Wrapper, Observable<?>>() {

@Override

public Observable<?> call(Wrapper wrapper) {

if ((wrapper.throwable instanceof ConnectException

|| wrapper.throwable instanceof SocketTimeoutException

|| wrapper.throwable instanceof TimeoutException)

&& wrapper.index < count + 1) { //如果超出重试次数也抛出错误,否则默认是会进入onCompleted

Log.e("tag","retry---->"+wrapper.index);

return Observable.timer(delay + (wrapper.index - 1) * increaseDelay, TimeUnit.MILLISECONDS);

}

return Observable.error(wrapper.throwable);

}

});

}

private class Wrapper {

private int index;

private Throwable throwable;

public Wrapper(Throwable throwable, int index) {

this.index = index;

this.throwable = throwable;

}

}

}

5.BaseEntity子类覆盖主要方法,实现处理的和返回的一一绑定

[html] view plain copy

在CODE上查看代码片派生到我的代码片

/**

* 测试接口请求数据

* Created by WZG on 2016/7/16.

*/

public class SubjectPost extends BaseEntity {

// 回调sub

private Subscriber mSubscriber;

private boolean all;

public SubjectPost(Subscriber getTopMovieOnNext, boolean all) {

this.mSubscriber = getTopMovieOnNext;

this.all = all;

}

@Override

public Observable getObservable(HttpService methods) {

return methods.getAllVedioBys(all);

}

@Override

public Subscriber getSubscirber() {

return mSubscriber;

}

}

6.然后定义service接口按照泛型的方式传递返回接口的数据

[html] view plain copy

在CODE上查看代码片派生到我的代码片

@POST("AppFiftyToneGraph/videoLink")

Observable<BaseResultEntity<List<Subject>>> getAllVedioBys(@Body boolean once_no);

7.定义返回数据的基础类,统一处理结果信息:

[html] view plain copy

在CODE上查看代码片派生到我的代码片

/**

* 回调信息统一封装类

* Created by WZG on 2016/7/16.

*/

public class BaseResultEntity<T> {

// 判断标示

private int ret;

// 提示信息

private String msg;

//显示数据(用户需要关心的数据)

private T data;}

8.封装回调的Subscriber统一处理加载框,错误提示,结果回调等处理

我们在subserice的四个回调方法中,统一处理onstart中我们现实加载框,onerror中统一处理系统错误和自定义错误信息,我们可以在baseentity的 funct1方法中统一处理返回信息,比如咱们测试方法我们可以统一判断ret参数,如果返回的是2我们抛出一个自定义的错误;或者返回的array数据为空我们也可以统一抛出一个空数据的异常!在oonext方法中我们返回一个泛型接口,传出最后service接口指定的泛型的对象!

[html] view plain copy

在CODE上查看代码片派生到我的代码片

/**

* 用于在Http请求开始时,自动显示一个ProgressDialog

* 在Http请求结束是,关闭ProgressDialog

* 调用者自己对请求数据进行处理

* Created by WZG on 2016/7/16.

*/

public class ProgressSubscriber<T> extends Subscriber<T> {

// 回调接口

private HttpOnNextListener mSubscriberOnNextListener;

// 弱引用反正内存泄露

private WeakReference<Context> mActivity;

// 是否能取消请求

private boolean cancel;

// 加载框可自己定义

private ProgressDialog pd;

public ProgressSubscriber(HttpOnNextListener mSubscriberOnNextListener, Context context) {

this.mSubscriberOnNextListener = mSubscriberOnNextListener;

this.mActivity = new WeakReference<>(context);

this.cancel = false;

initProgressDialog();

}

public ProgressSubscriber(HttpOnNextListener mSubscriberOnNextListener, Context context, boolean cancel) {

this.mSubscriberOnNextListener = mSubscriberOnNextListener;

this.mActivity = new WeakReference<>(context);

this.cancel = cancel;

initProgressDialog();

}

/**

* 初始化加载框

*/

private void initProgressDialog() {

Context context = mActivity.get();

if (pd == null && context != null) {

pd = new ProgressDialog(context);

pd.setCancelable(cancel);

if (cancel) {

pd.setOnCancelListener(new DialogInterface.OnCancelListener() {

@Override

public void onCancel(DialogInterface dialogInterface) {

onCancelProgress();

}

});

}

}

}

/**

* 显示加载框

*/

private void showProgressDialog() {

Context context = mActivity.get();

if (pd == null || context == null) return;

if (!pd.isShowing()) {

pd.show();

}

}

/**

* 隐藏

*/

private void dismissProgressDialog() {

if (pd != null && pd.isShowing()) {

pd.dismiss();

}

}

/**

* 订阅开始时调用

* 显示ProgressDialog

*/

@Override

public void onStart() {

showProgressDialog();

}

/**

* 完成,隐藏ProgressDialog

*/

@Override

public void onCompleted() {

dismissProgressDialog();

}

/**

* 对错误进行统一处理

* 隐藏ProgressDialog

*

* @param e

*/

@Override

public void onError(Throwable e) {

Context context = mActivity.get();

if (context == null) return;

if (e instanceof SocketTimeoutException) {

Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();

} else if (e instanceof ConnectException) {

Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();

} else {

Toast.makeText(context, "错误" + e.getMessage(), Toast.LENGTH_SHORT).show();

Log.i("tag", "error----------->" + e.toString());

}

dismissProgressDialog();

}

/**

* 将onNext方法中的返回结果交给Activity或Fragment自己处理

*

* @param t 创建Subscriber时的泛型类型

*/

@Override

public void onNext(T t) {

if (mSubscriberOnNextListener != null) {

mSubscriberOnNextListener.onNext(t);

}

}

/**

* 取消ProgressDialog的时候,取消对observable的订阅,同时也取消了http请求

*/

public void onCancelProgress() {

if (!this.isUnsubscribed()) {

this.unsubscribe();

}

}

}

9.自定义返回接口,泛型传递返回值

[html] view plain copy

在CODE上查看代码片派生到我的代码片

/**

* 成功回调处理

* Created by WZG on 2016/7/16.

*/

public interface HttpOnNextListener<T> {

void onNext(T t);

}

10.发起http请求处理

定义一个指定类型的回调类httpOnextListener,然后初始化一个统一处理返回结果的ProgressSubscriber对象,将sub传入给封装的BaseEntity对象中,发起网络请求,通过Rxjava后台处理网络请求,主线程处理返回结果;网络请求onext前,调用BaseEntity的fun1方法,统一判断返回信息(可预处理错误),最后通过自定义泛型httpOnextListener返回成功数据

[html] view plain copy

在CODE上查看代码片派生到我的代码片

// 完美封装简化版

private void simpleDo() {

SubjectPost postEntity = new SubjectPost(new ProgressSubscriber(simpleOnNextListener, this), true);

HttpManager manager = HttpManager.getInstance();

manager.doHttpDeal(postEntity);

}

// 回调一一对应

HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<Subject>>() {

@Override

public void onNext(List<Subject> subjects) {

tvMsg.setText("已封装:\n" + subjects.toString());

}

};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐