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

背景:
学习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)
.addConverterFactory(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());
}
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值