RxJava+Retrofit+OkHttp深入浅出-终极封装二(网络请求)
CSDN
博客发布了一系列的RxJava+Retrofit+OkHttp
深入浅出-终极封装 之前发出后收到很多朋友的关注,原本只是自己学习后的一些经验总结,但是有同学运用到实战当中,这让我很惶恐,所有后续一直更新了很多次版本,有些地方难免有所变动导致之前的博客有所出入,正好最近受到掘金邀请内测博客,所以决定重新写一版,按照最后迭代完成的封装详细的讲述一遍,欢迎大家关注!
注意:由于本章的特殊性,后续文章比较长而且复杂,涉及内容也很多,所以大家准备好茶水,前方高能预警。
封装成果
封装完以后,具有如下功能:
-
1.Retrofit+Rxjava+okhttp基本使用方法
-
2.统一处理请求数据格式
-
3.统一的ProgressDialog和回调Subscriber处理
-
4.取消http请求
-
5.预处理http请求
-
6.返回数据的统一判断
-
7.失败后的retry封装处理
-
8.RxLifecycle管理生命周期,防止泄露
实现效果:
具体使用
封装后http请求代码如下
-
// 完美封装简化版
-
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>>() {
-
-
public void onNext(List<Subject> subjects) {
-
tvMsg.setText( "已封装:\n" + subjects.toString());
-
}
-
-
/*用户主动调用,默认是不需要覆写该方法*/
-
-
public void onError(Throwable e) {
-
super.onError(e);
-
tvMsg.setText( "失败:\n" + e.toString());
-
}
-
};
是不是很简单?你可能说这还简单,好咱们对比一下正常使用Retrofit
的方法
-
/**
-
* 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>() {
-
-
public void onCompleted() {
-
if (pd != null && pd.isShowing()) {
-
pd.dismiss();
-
}
-
}
-
-
-
public void onError(Throwable e) {
-
if (pd != null && pd.isShowing()) {
-
pd.dismiss();
-
}
-
}
-
-
-
public void onNext(RetrofitEntity retrofitEntity) {
-
tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
-
}
-
-
-
public void onStart() {
-
super.onStart();
-
pd.show();
-
}
-
}
-
-
);
-
}
可能你发现确是代码有点多,但是更加可怕的是,如果你一个activity或者fragment中多次需要http请求,你需要多次重复的写回调处理(一个回到就有4个方法呀!!!!反正我是忍受不了),而且以上处理还没有做过多的判断和错误校验就如此复杂!~好了介绍完了,开始咱们的优化之路吧!
项目结构:
RxJava
如果你对RxJava
不了解,好吧骚年赶快学学吧,不然真会out
了,下面给出博主当初学习RxJava
的一些资源:
Retrofit
咱家今天的主角来了,咱们也深入浅出一下了解下Retrofit
使用,前方高能,如果你是深度Retrofit
选手请直接跳过本节!!!
1.首先确保在AndroidManifest.xml中请求了网络权限
<uses-permission android:name="android.permission.INTERNET"/>
2.在app/build.gradle添加引用
-
/*rx-android-java*/
-
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
-
compile 'com.trello:rxlifecycle:1.0'
-
compile 'com.trello:rxlifecycle-components:1.0'
-
/*rotrofit*/
-
compile 'com.squareup.retrofit2:retrofit:2.1.0'
-
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
-
compile 'com.google.code.gson:gson:2.8.0'
3.常用注解
这里介绍一些常用的注解的使用
@Query
、@QueryMap
:用于Http Get
请求传递参数@Field
:用于Post
方式传递参数,需要在请求接口方法上添加@FormUrlEncoded
,即以表单的方式传递参数@Body
:用于Post
,根据转换方式将实例对象转化为对应字符串传递参数.比如Retrofit
添加GsonConverterFactory
则是将body
转化为gson
字符串进行传递@Path
:用于URL
上占位符@Part
:配合@Multipart
使用,一般用于文件上传@Header
:添加http header
@Headers
:跟@Header
作用一样,只是使用方式不一样,@Header
是作为请求方法的参数传入,@Headers
是以固定方式直接添加到请求方法上
ReTrofit
基本使用:
首先给定一个测试接口文档,后面的博客中我们都是用这个接口调试
-
/**
-
* @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
(通常情况下就是域名)。
-
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
, 等等。) 方法的注解以及用于分发网络调用的方法。而且这些方法的参数也可以有特殊的注解。
-
/**
-
* 接口地址
-
* Created by WZG on 2016/7/16.
-
*/
-
public interface MyApiEndpointInterface {
-
"AppFiftyToneGraph/videoLink")(
-
Call<RetrofitEntity> getAllVedio(@Body boolean once_no)
-
}
3.得到call
然后同步处理处理回调:
-
MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class);
-
Call<RetrofitEntity> call = apiService.getAllVedio( true);
-
call.enqueue( new Callback<RetrofitEntity>() {
-
-
public void onResponse(Response<RetrofitEntity> response, Retrofit retrofit) {
-
RetrofitEntity entity = response.body();
-
Log.i( "tag", "onResponse----->" + entity.getMsg());
-
}
-
-
-
public void onFailure(Throwable t) {
-
Log.i( "tag", "onFailure----->" + t.toString());
-
-
}
-
});
这就是简单的Retrofit
使用步骤,接下来我们结合RxJava讲述
ReTrofit+Rxjava
基本使用
对比之前的Retrofit
使用
1.在于我们需要修改service
接口返回信息我们需要返回一个Observable
对象
-
"AppFiftyToneGraph/videoLink")(
-
Observable<RetrofitEntity> getAllVedioBy(@Body boolean once_no);
2.然后初始化Retrofit
需要添加对Rxjava
的适配,注意一定要retrofit2
才有这个功能哦
-
Retrofit retrofit = new Retrofit.Builder()
-
.client(builder.build())
-
.addConverterFactory(GsonConverterFactory.create())
-
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
-
.baseUrl(HttpManager.BASE_URL)
-
.build();
3.回调通过RxJava
处理
-
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>() {
-
-
public void onCompleted() {
-
}
-
-
-
public void onError(Throwable e) {
-
}
-
-
-
public void onNext(RetrofitEntity retrofitEntity) {
-
tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
-
}
-
}
-
-
);
简单的RxJava
集合Retrofit
的使用就介绍完了,同样的可以发现使用起来很多重复性的代码,而且使用也不是那么简单,所以才有了下面的封装
ReTrofit+Rxjava进阶封装之路
先来一张流程图压压惊
请求数据封装
1.参数
首先需要封装的使我们的数据类,在数据类中需要封装请求中用到的相关数据的设置,比如请求参数、方法、加载框显示设置等等
-
public abstract class BaseApi<T> implements Func1<BaseResultEntity<T>, T> {
-
//rx生命周期管理
-
private SoftReference<RxAppCompatActivity> rxAppCompatActivity;
-
/*回调*/
-
private SoftReference<HttpOnNextListener> listener;
-
/*是否能取消加载框*/
-
private boolean cancel;
-
/*是否显示加载框*/
-
private boolean showProgress;
-
/*是否需要缓存处理*/
-
private boolean cache;
-
/*基础url*/
-
private String baseUrl= "http://www.izaodao.com/Api/";
-
/*方法-如果需要缓存必须设置这个参数;不需要不用設置*/
-
private String mothed;
-
/*超时时间-默认6秒*/
-
private int connectionTime = 6;
-
/*有网情况下的本地缓存时间默认60秒*/
-
private int cookieNetWorkTime= 60;
-
/*无网络的情况下本地缓存时间默认30天*/
-
private int cookieNoNetWorkTime= 24* 60* 60* 30;
-
}
注释很详细,这里不具体描述了,由于这里是最后封装完成以后的代码,所以有些内容本章还会部分不会涉及,因为功能太多,还是按照一开始的博客章节讲解。
2.抽象api
接口
-
/**
-
* 设置参数
-
*
-
* @param retrofit
-
* @return
-
*/
-
public abstract Observable getObservable(Retrofit retrofit);
通过子类也即是我们的具体api
接口,通过getObservable
实现service
中定义的接口方法,例如:
-
public class SubjectPostApi extends BaseApi {
-
xxxxxxx
-
xxxxxxx
-
-
-
public Observable getObservable(Retrofit retrofit) {
-
HttpPostService service = retrofit.create(HttpPostService.class);
-
return service.getAllVedioBys(isAll());
-
}
-
}
通过传入的Retrofit
对象,可以随意切换挑选Service
对象,得到定义的注解方法,初始完成以后返回Observable
对象。
3.结果判断
这里结合RxJava
的map
方法在服务器返回数据中,统一处理数据处理,所以BaseApi<T> implements Func1<BaseResultEntity<T>, T>
,后边结合结果处理链接起来使用
-
-
public T call(BaseResultEntity<T> httpResult) {
-
if (httpResult.getRet() == 0) {
-
throw new HttpTimeException(httpResult.getMsg());
-
}
-
return httpResult.getData();
-
}
由于测试接口,也是当前我们公司接口都是有统一规则的,想必大家都有这样的接口规则,所以才有这里的统一判断,规则如下:
-
* ret: 1成功, 2失败
-
* msg:信息
-
* data:{
-
* name:视频名称
-
* title:标题
-
* }
其实上面的接口文档中就介绍了,统一先通过ret
判断,失败显示msg
信息,data
是成功后的数据也就是用户关心的数据,所以可封装一个结果对象BaseResultEntity
.
4.结果数据
-
/**
-
* 回调信息统一封装类
-
* Created by WZG on 2016/7/16.
-
*/
-
public class BaseResultEntity<T> {
-
// 判断标示
-
private int ret;
-
// 提示信息
-
private String msg;
-
//显示数据(用户需要关心的数据)
-
private T data;
-
-
-
xxxxx get-set xxxxx
-
}
这里结合BaseApi
的Func1
判断,失败直接抛出一个异常,交个RxJava
的onError
处理,成功则将用户关心的数据传给Gson
解析返回
5.泛型传递
BaseResultEntity<T>
中的泛型T
也就是我们所关心的回调数据,同样也是Gson最后解析返回的数据,传递的过程根节点是通过定义service
方法是给定的,例如:
-
public interface HttpPostService {
-
"AppFiftyToneGraph/videoLink")(
-
Call<RetrofitEntity> getAllVedio(@Body boolean once_no);
-
}
其中的RetrofitEntity
就是用户关心的数据类,通过泛型传递给最后的接口。
6.强调
很多兄弟通过QQ群反馈给我说,使用一个接口需要写一个对应的api
类继承BaseApi
是不是很麻烦,我这里强调一下,这样封装是为了将一个Api
接口作为一个对象去封装,个人觉得有必要封装成一个类,在日后工程日益增加接口随着增加的同时,对象的做法更加有利于查找接口和修改接口有利于迭代。
操作类封装
1初始对象
首先初始化一个单利方便HttpManager
请求;这里用了volatile
的对象,不懂的同学可以参考我的另一篇博客
-
private volatile static HttpManager INSTANCE;
-
-
//构造方法私有
-
private HttpManager() {
-
}
-
-
//获取单例
-
public static HttpManager getInstance() {
-
if (INSTANCE == null) {
-
synchronized (HttpManager.class) {
-
if (INSTANCE == null) {
-
INSTANCE = new HttpManager();
-
}
-
}
-
}
-
return INSTANCE;
-
}
2接口处理和回调处理:
-
/**
-
* 处理http请求
-
*
-
* @param basePar 封装的请求数据
-
*/
-
public void doHttpDeal(BaseApi basePar) {
-
//手动创建一个OkHttpClient并设置超时时间缓存等设置
-
OkHttpClient.Builder builder = new OkHttpClient.Builder();
-
builder.connectTimeout(basePar.getConnectionTime(), TimeUnit.SECONDS);
-
builder.addInterceptor( new CookieInterceptor(basePar.isCache()));
-
-
/*创建retrofit对象*/
-
Retrofit retrofit = new Retrofit.Builder()
-
.client(builder.build())
-
.addConverterFactory(GsonConverterFactory.create())
-
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
-
.baseUrl(basePar.getBaseUrl())
-
.build();
-
-
-
/*rx处理*/
-
ProgressSubscriber subscriber = new ProgressSubscriber(basePar);
-
Observable observable = basePar.getObservable(retrofit)
-
/*失败后的retry配置*/
-
.retryWhen( new RetryWhenNetworkException())
-
/*生命周期管理*/
-
.compose(basePar.getRxAppCompatActivity().bindToLifecycle())
-
/*http请求线程*/
-
.subscribeOn(Schedulers.io())
-
.unsubscribeOn(Schedulers.io())
-
/*回调线程*/
-
.observeOn(AndroidSchedulers.mainThread())
-
/*结果判断*/
-
.map(basePar);
-
-
/*数据回调*/
-
observable.subscribe(subscriber);
-
}
首先通过api
接口类BaseApi
的实现类中数据初始化OkHttpClient
和Retrofit
对象,其中包含了url
,超时等,接着通过BaseApi
的抽象方法getObservable
得到Observable
对象,得到Observable
对象以后,我们就能随意的切换现成来处理,整个请求通过compose
设定的rxlifecycle
来管理生命周期,所以不会溢出和泄露无需任何担心,最后再服务器数据返回时,通过map
判断结果,剔除错误信息,成功以后返回到自定义的ProgressSubscriber
对象中,所以接下来封装ProgressSubscriber
对象。
ProgressSubscriber
封装
ProgressSubscriber
其实是继承于Subscriber
,封装的方法无非是对Subscriber
的回调方法的封装
- onStart():开始
- onCompleted():结束
- onError(Throwable e):错误
- onNext(T t):成功
1.请求加载框
http
请求都伴随着加载框的使用,所以这里需要在onStart()
使用前初始一个加载框,这里简单的用ProgressDialog
代替
-
/**
-
* 用于在Http请求开始时,自动显示一个ProgressDialog
-
* 在Http请求结束是,关闭ProgressDialog
-
* 调用者自己对请求数据进行处理
-
* Created by WZG on 2016/7/16.
-
*/
-
public class ProgressSubscriber<T> extends Subscriber<T> {
-
/*是否弹框*/
-
private boolean showPorgress = true;
-
/* 软引用回调接口*/
-
private SoftReference<HttpOnNextListener> mSubscriberOnNextListener;
-
/*软引用反正内存泄露*/
-
private SoftReference<RxAppCompatActivity> mActivity;
-
/*加载框可自己定义*/
-
private ProgressDialog pd;
-
/*请求数据*/
-
private BaseApi api;
-
-
-
/**
-
* 构造
-
*
-
* @param api
-
*/
-
public ProgressSubscriber(BaseApi api) {
-
this.api = api;
-
this.mSubscriberOnNextListener = api.getListener();
-
this.mActivity = new SoftReference<>(api.getRxAppCompatActivity());
-
setShowPorgress(api.isShowProgress());
-
if (api.isShowProgress()) {
-
initProgressDialog(api.isCancel());
-
}
-
}
-
-
-
/**
-
* 初始化加载框
-
*/
-
private void initProgressDialog(boolean cancel) {
-
Context context = mActivity.get();
-
if (pd == null && context != null) {
-
pd = new ProgressDialog(context);
-
pd.setCancelable(cancel);
-
if (cancel) {
-
pd.setOnCancelListener( new DialogInterface.OnCancelListener() {
-
-
public void onCancel(DialogInterface dialogInterface) {
-
onCancelProgress();
-
}
-
});
-
}
-
}
-
}
-
-
-
/**
-
* 显示加载框
-
*/
-
private void showProgressDialog() {
-
if (!isShowPorgress()) return;
-
Context context = mActivity.get();
-
if (pd == null || context == null) return;
-
if (!pd.isShowing()) {
-
pd.show();
-
}
-
}
-
-
-
/**
-
* 隐藏
-
*/
-
private void dismissProgressDialog() {
-
if (!isShowPorgress()) return;
-
if (pd != null && pd.isShowing()) {
-
pd.dismiss();
-
}
-
}
-
}
由于progress
的特殊性,需要指定content
而且不能是Application
所以这里传递一个RxAppCompatActivity
,而同时上面的HttpManager
同样需要,所以这里统一还是按照BaseApi
传递过来,使用软引用的方式避免泄露。剩下的无非是初始化,显示和关闭方法,可以详细看代码。
2.onStart()
实现
在onStart()
中需要调用加载框,然后这里还有网络缓存的逻辑,后面会单独讲解,现在先忽略它的存在。
-
/**
-
* 订阅开始时调用
-
* 显示ProgressDialog
-
*/
-
-
public void onStart() {
-
showProgressDialog();
-
/*缓存并且有网*/
-
if (api.isCache() && AppUtil.isNetworkAvailable(RxRetrofitApp.getApplication())) {
-
/*获取缓存数据*/
-
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();
-
}
-
}
-
}
-
}
3.onCompleted()实现
-
/**
-
* 完成,隐藏ProgressDialog
-
*/
-
-
public void onCompleted() {
-
dismissProgressDialog();
-
}
4.onError(Throwable e)实现
在onError(Throwable e)
是对错误信息的处理和缓存读取的处理,后续会讲解,先忽略。
-
/**
-
* 对错误进行统一处理
-
* 隐藏ProgressDialog
-
*
-
* @param e
-
*/
-
-
public void onError(Throwable e) {
-
dismissProgressDialog();
-
/*需要緩存并且本地有缓存才返回*/
-
if (api.isCache()) {
-
Observable.just(api.getUrl()).subscribe( new Subscriber<String>() {
-
-
public void onCompleted() {
-
-
}
-
-
-
public void onError(Throwable e) {
-
errorDo(e);
-
}
-
-
-
public void onNext(String s) {
-
/*获取缓存数据*/
-
CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(s);
-
if (cookieResulte == null) {
-
throw new HttpTimeException( "网络错误");
-
}
-
long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
-
if (time < api.getCookieNoNetWorkTime()) {
-
if (mSubscriberOnNextListener.get() != null) {
-
mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
-
}
-
} else {
-
CookieDbUtil.getInstance().deleteCookie(cookieResulte);
-
throw new HttpTimeException( "网络错误");
-
}
-
}
-
});
-
} else {
-
errorDo(e);
-
}
-
}
-
-
/*错误统一处理*/
-
private void errorDo(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();
-
}
-
if (mSubscriberOnNextListener.get() != null) {
-
mSubscriberOnNextListener.get().onError(e);
-
}
-
}
5.onNext(T t)
实现
-
/**
-
* 将onNext方法中的返回结果交给Activity或Fragment自己处理
-
*
-
* @param t 创建Subscriber时的泛型类型
-
*/
-
-
public void onNext(T t) {
-
if (mSubscriberOnNextListener.get() != null) {
-
mSubscriberOnNextListener.get().onNext(t);
-
}
-
}
主要是是将得到的结果,通过自定义的接口返回给view
界面,其中的软引用对象mSubscriberOnNextListener
是自定义的接口回调类HttpOnNextListener
.
6.HttpOnNextListener
封装
现在只需关心onNext(T t)
和onError(Throwable e)
接口即可,回调的触发点都是在上面的ProgressSubscriber
中调用
-
/**
-
* 成功回调处理
-
* Created by WZG on 2016/7/16.
-
*/
-
public abstract class HttpOnNextListener<T> {
-
/**
-
* 成功后回调方法
-
* @param t
-
*/
-
public abstract void onNext(T t);
-
-
/**
-
* 緩存回調結果
-
* @param string
-
*/
-
public void onCacheNext(String string){
-
-
}
-
-
/**
-
* 失败或者错误方法
-
* 主动调用,更加灵活
-
* @param e
-
*/
-
public void onError(Throwable e){
-
-
}
-
-
/**
-
* 取消回調
-
*/
-
public void onCancel(){
-
-
}
-
}
失败后的retry
处理
这里你可能会问,Retrofit
有自带的retry
处理呀,的确Retrofit
有自带的retry
处理,但是有很多的局限,先看下使用
-
OkHttpClient.Builder builder = new OkHttpClient.Builder();
-
builder.retryOnConnectionFailure( true);
使用起来还是很方便,只需要调用一个方法即可,但是它是不可控的,也就是没有办法设置retry
时间次数,所以不太灵活,既然如此还不如自己封装一下,因为用RxJava
实现这个简直小菜,无形中好像已经给RxJava
打了广告,中毒太深。
很简单直接上代码:
-
/**
-
* retry条件
-
* Created by WZG on 2016/10/17.
-
*/
-
public class RetryWhenNetworkException implements Func1<Observable<? extends Throwable>, Observable<?>> {
-
// retry次数
-
private int count = 3;
-
// 延迟
-
private long delay = 3000;
-
// 叠加延迟
-
private long increaseDelay = 3000;
-
-
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;
-
}
-
-
-
public Observable<?> call(Observable<? extends Throwable> observable) {
-
return observable
-
.zipWith(Observable.range( 1, count + 1), new Func2<Throwable, Integer, Wrapper>() {
-
-
public Wrapper call(Throwable throwable, Integer integer) {
-
return new Wrapper(throwable, integer);
-
}
-
}).flatMap( new Func1<Wrapper, Observable<?>>() {
-
-
public Observable<?> call(Wrapper wrapper) {
-
if ((wrapper.throwable instanceof ConnectException
-
|| wrapper.throwable instanceof SocketTimeoutException
-
|| wrapper.throwable instanceof TimeoutException)
-
&& wrapper.index < count + 1) { //如果超出重试次数也抛出错误,否则默认是会进入onCompleted
-
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;
-
}
-
}
-
}
使用
到这里,我们第一步封装已经完成了,下面讲解下如何使用,已经看明白的各位看官,估计早就看明白了使用方式,无非是创建一个api
对象继承BaseApi
初始接口信息,然后调用HttpManager
对象的doHttpDeal(BaseApi basePar)
方法,最后静静的等待回调类HttpOnNextListener<T>
类返回的onNext(T t)
成功数据或者onError(Throwable e)
数据。
其实代码就是这样:
api
接口对象
-
/**
-
* 测试数据
-
* Created by WZG on 2016/7/16.
-
*/
-
public class SubjectPostApi extends BaseApi {
-
// 接口需要传入的参数 可自定义不同类型
-
private boolean all;
-
/*任何你先要传递的参数*/
-
// String xxxxx;
-
-
/**
-
* 默认初始化需要给定回调和rx周期类
-
* 可以额外设置请求设置加载框显示,回调等(可扩展)
-
* @param listener
-
* @param rxAppCompatActivity
-
*/
-
public SubjectPostApi(HttpOnNextListener listener, RxAppCompatActivity rxAppCompatActivity) {
-
super(listener,rxAppCompatActivity);
-
setShowProgress( true);
-
setCancel( true);
-
setCache( true);
-
setMothed( "AppFiftyToneGraph/videoLink");
-
setCookieNetWorkTime( 60);
-
setCookieNoNetWorkTime( 24* 60* 60);
-
}
-
-
public boolean isAll() {
-
return all;
-
}
-
-
public void setAll(boolean all) {
-
this.all = all;
-
}
-
-
-
public Observable getObservable(Retrofit retrofit) {
-
HttpPostService service = retrofit.create(HttpPostService.class);
-
return service.getAllVedioBys(isAll());
-
}
-
}
请求回调
-
-
// 完美封装简化版
-
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>>() {
-
-
public void onNext(List<SubjectResulte> subjects) {
-
tvMsg.setText( "网络返回:\n" + subjects.toString());
-
}
-
-
-
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() );
-
}
-
-
/*用户主动调用,默认是不需要覆写该方法*/
-
-
public void onError(Throwable e) {
-
super.onError(e);
-
tvMsg.setText( "失败:\n" + e.toString());
-
}
-
-
/*用户主动调用,默认是不需要覆写该方法*/
-
-
public void onCancel() {
-
super.onCancel();
-
tvMsg.setText( "取消請求");
-
}
-
};
后续
到这里,封装功能中很多功能还没涉及和讲解,后续会陆续更新!
先给大家看看为师的完全体功能:
-
1.Retrofit+Rxjava+okhttp基本使用方法
-
2.统一处理请求数据格式
-
3.统一的ProgressDialog和回调Subscriber处理
-
4.取消http请求
-
5.预处理http请求
-
6.返回数据的统一判断
-
7.失败后的retry处理
-
8.RxLifecycle管理生命周期,防止泄露
-
9.文件上传下载(支持多文件,断点续传)
-
10.Cache数据持久化和数据库(greenDao)两种缓存机制
-
11.异常统一处理
来个图压压惊:
迫不及待的小伙伴可以看这里:
但是其中有些后续优化迭代未及时更新,别生气,我马上补!
源码:
建议
RxJava+Retrofit+OkHttp深入浅出-终极封装二(网络请求)
CSDN
博客发布了一系列的RxJava+Retrofit+OkHttp
深入浅出-终极封装 之前发出后收到很多朋友的关注,原本只是自己学习后的一些经验总结,但是有同学运用到实战当中,这让我很惶恐,所有后续一直更新了很多次版本,有些地方难免有所变动导致之前的博客有所出入,正好最近受到掘金邀请内测博客,所以决定重新写一版,按照最后迭代完成的封装详细的讲述一遍,欢迎大家关注!
注意:由于本章的特殊性,后续文章比较长而且复杂,涉及内容也很多,所以大家准备好茶水,前方高能预警。
封装成果
封装完以后,具有如下功能:
-
1.Retrofit+Rxjava+okhttp基本使用方法
-
2.统一处理请求数据格式
-
3.统一的ProgressDialog和回调Subscriber处理
-
4.取消http请求
-
5.预处理http请求
-
6.返回数据的统一判断
-
7.失败后的retry封装处理
-
8.RxLifecycle管理生命周期,防止泄露
实现效果:
具体使用
封装后http请求代码如下
-
// 完美封装简化版
-
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>>() {
-
-
public void onNext(List<Subject> subjects) {
-
tvMsg.setText( "已封装:\n" + subjects.toString());
-
}
-
-
/*用户主动调用,默认是不需要覆写该方法*/
-
-
public void onError(Throwable e) {
-
super.onError(e);
-
tvMsg.setText( "失败:\n" + e.toString());
-
}
-
};
是不是很简单?你可能说这还简单,好咱们对比一下正常使用Retrofit
的方法
-
/**
-
* 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>() {
-
-
public void onCompleted() {
-
if (pd != null && pd.isShowing()) {
-
pd.dismiss();
-
}
-
}
-
-
-
public void onError(Throwable e) {
-
if (pd != null && pd.isShowing()) {
-
pd.dismiss();
-
}
-
}
-
-
-
public void onNext(RetrofitEntity retrofitEntity) {
-
tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
-
}
-
-
-
public void onStart() {
-
super.onStart();
-
pd.show();
-
}
-
}
-
-
);
-
}
可能你发现确是代码有点多,但是更加可怕的是,如果你一个activity或者fragment中多次需要http请求,你需要多次重复的写回调处理(一个回到就有4个方法呀!!!!反正我是忍受不了),而且以上处理还没有做过多的判断和错误校验就如此复杂!~好了介绍完了,开始咱们的优化之路吧!
项目结构:
RxJava
如果你对RxJava
不了解,好吧骚年赶快学学吧,不然真会out
了,下面给出博主当初学习RxJava
的一些资源:
Retrofit
咱家今天的主角来了,咱们也深入浅出一下了解下Retrofit
使用,前方高能,如果你是深度Retrofit
选手请直接跳过本节!!!
1.首先确保在AndroidManifest.xml中请求了网络权限
<uses-permission android:name="android.permission.INTERNET"/>
2.在app/build.gradle添加引用
-
/*rx-android-java*/
-
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
-
compile 'com.trello:rxlifecycle:1.0'
-
compile 'com.trello:rxlifecycle-components:1.0'
-
/*rotrofit*/
-
compile 'com.squareup.retrofit2:retrofit:2.1.0'
-
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
-
compile 'com.google.code.gson:gson:2.8.0'
3.常用注解
这里介绍一些常用的注解的使用
@Query
、@QueryMap
:用于Http Get
请求传递参数@Field
:用于Post
方式传递参数,需要在请求接口方法上添加@FormUrlEncoded
,即以表单的方式传递参数@Body
:用于Post
,根据转换方式将实例对象转化为对应字符串传递参数.比如Retrofit
添加GsonConverterFactory
则是将body
转化为gson
字符串进行传递@Path
:用于URL
上占位符@Part
:配合@Multipart
使用,一般用于文件上传@Header
:添加http header
@Headers
:跟@Header
作用一样,只是使用方式不一样,@Header
是作为请求方法的参数传入,@Headers
是以固定方式直接添加到请求方法上
ReTrofit
基本使用:
首先给定一个测试接口文档,后面的博客中我们都是用这个接口调试
-
/**
-
* @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
(通常情况下就是域名)。
-
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
, 等等。) 方法的注解以及用于分发网络调用的方法。而且这些方法的参数也可以有特殊的注解。
-
/**
-
* 接口地址
-
* Created by WZG on 2016/7/16.
-
*/
-
public interface MyApiEndpointInterface {
-
"AppFiftyToneGraph/videoLink")(
-
Call<RetrofitEntity> getAllVedio(@Body boolean once_no)
-
}
3.得到call
然后同步处理处理回调:
-
MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class);
-
Call<RetrofitEntity> call = apiService.getAllVedio( true);
-
call.enqueue( new Callback<RetrofitEntity>() {
-
-
public void onResponse(Response<RetrofitEntity> response, Retrofit retrofit) {
-
RetrofitEntity entity = response.body();
-
Log.i( "tag", "onResponse----->" + entity.getMsg());
-
}
-
-
-
public void onFailure(Throwable t) {
-
Log.i( "tag", "onFailure----->" + t.toString());
-
-
}
-
});
这就是简单的Retrofit
使用步骤,接下来我们结合RxJava讲述
ReTrofit+Rxjava
基本使用
对比之前的Retrofit
使用
1.在于我们需要修改service
接口返回信息我们需要返回一个Observable
对象
-
"AppFiftyToneGraph/videoLink")(
-
Observable<RetrofitEntity> getAllVedioBy(@Body boolean once_no);
2.然后初始化Retrofit
需要添加对Rxjava
的适配,注意一定要retrofit2
才有这个功能哦
-
Retrofit retrofit = new Retrofit.Builder()
-
.client(builder.build())
-
.addConverterFactory(GsonConverterFactory.create())
-
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
-
.baseUrl(HttpManager.BASE_URL)
-
.build();
3.回调通过RxJava
处理
-
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>() {
-
-
public void onCompleted() {
-
}
-
-
-
public void onError(Throwable e) {
-
}
-
-
-
public void onNext(RetrofitEntity retrofitEntity) {
-
tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
-
}
-
}
-
-
);
简单的RxJava
集合Retrofit
的使用就介绍完了,同样的可以发现使用起来很多重复性的代码,而且使用也不是那么简单,所以才有了下面的封装
ReTrofit+Rxjava进阶封装之路
先来一张流程图压压惊
请求数据封装
1.参数
首先需要封装的使我们的数据类,在数据类中需要封装请求中用到的相关数据的设置,比如请求参数、方法、加载框显示设置等等
-
public abstract class BaseApi<T> implements Func1<BaseResultEntity<T>, T> {
-
//rx生命周期管理
-
private SoftReference<RxAppCompatActivity> rxAppCompatActivity;
-
/*回调*/
-
private SoftReference<HttpOnNextListener> listener;
-
/*是否能取消加载框*/
-
private boolean cancel;
-
/*是否显示加载框*/
-
private boolean showProgress;
-
/*是否需要缓存处理*/
-
private boolean cache;
-
/*基础url*/
-
private String baseUrl= "http://www.izaodao.com/Api/";
-
/*方法-如果需要缓存必须设置这个参数;不需要不用設置*/
-
private String mothed;
-
/*超时时间-默认6秒*/
-
private int connectionTime = 6;
-
/*有网情况下的本地缓存时间默认60秒*/
-
private int cookieNetWorkTime= 60;
-
/*无网络的情况下本地缓存时间默认30天*/
-
private int cookieNoNetWorkTime= 24* 60* 60* 30;
-
}
注释很详细,这里不具体描述了,由于这里是最后封装完成以后的代码,所以有些内容本章还会部分不会涉及,因为功能太多,还是按照一开始的博客章节讲解。
2.抽象api
接口
-
/**
-
* 设置参数
-
*
-
* @param retrofit
-
* @return
-
*/
-
public abstract Observable getObservable(Retrofit retrofit);
通过子类也即是我们的具体api
接口,通过getObservable
实现service
中定义的接口方法,例如:
-
public class SubjectPostApi extends BaseApi {
-
xxxxxxx
-
xxxxxxx
-
-
-
public Observable getObservable(Retrofit retrofit) {
-
HttpPostService service = retrofit.create(HttpPostService.class);
-
return service.getAllVedioBys(isAll());
-
}
-
}
通过传入的Retrofit
对象,可以随意切换挑选Service
对象,得到定义的注解方法,初始完成以后返回Observable
对象。
3.结果判断
这里结合RxJava
的map
方法在服务器返回数据中,统一处理数据处理,所以BaseApi<T> implements Func1<BaseResultEntity<T>, T>
,后边结合结果处理链接起来使用
-
-
public T call(BaseResultEntity<T> httpResult) {
-
if (httpResult.getRet() == 0) {
-
throw new HttpTimeException(httpResult.getMsg());
-
}
-
return httpResult.getData();
-
}
由于测试接口,也是当前我们公司接口都是有统一规则的,想必大家都有这样的接口规则,所以才有这里的统一判断,规则如下:
-
* ret: 1成功, 2失败
-
* msg:信息
-
* data:{
-
* name:视频名称
-
* title:标题
-
* }
其实上面的接口文档中就介绍了,统一先通过ret
判断,失败显示msg
信息,data
是成功后的数据也就是用户关心的数据,所以可封装一个结果对象BaseResultEntity
.
4.结果数据
-
/**
-
* 回调信息统一封装类
-
* Created by WZG on 2016/7/16.
-
*/
-
public class BaseResultEntity<T> {
-
// 判断标示
-
private int ret;
-
// 提示信息
-
private String msg;
-
//显示数据(用户需要关心的数据)
-
private T data;
-
-
-
xxxxx get-set xxxxx
-
}
这里结合BaseApi
的Func1
判断,失败直接抛出一个异常,交个RxJava
的onError
处理,成功则将用户关心的数据传给Gson
解析返回
5.泛型传递
BaseResultEntity<T>
中的泛型T
也就是我们所关心的回调数据,同样也是Gson最后解析返回的数据,传递的过程根节点是通过定义service
方法是给定的,例如:
-
public interface HttpPostService {
-
"AppFiftyToneGraph/videoLink")(
-
Call<RetrofitEntity> getAllVedio(@Body boolean once_no);
-
}
其中的RetrofitEntity
就是用户关心的数据类,通过泛型传递给最后的接口。
6.强调
很多兄弟通过QQ群反馈给我说,使用一个接口需要写一个对应的api
类继承BaseApi
是不是很麻烦,我这里强调一下,这样封装是为了将一个Api
接口作为一个对象去封装,个人觉得有必要封装成一个类,在日后工程日益增加接口随着增加的同时,对象的做法更加有利于查找接口和修改接口有利于迭代。
操作类封装
1初始对象
首先初始化一个单利方便HttpManager
请求;这里用了volatile
的对象,不懂的同学可以参考我的另一篇博客
-
private volatile static HttpManager INSTANCE;
-
-
//构造方法私有
-
private HttpManager() {
-
}
-
-
//获取单例
-
public static HttpManager getInstance() {
-
if (INSTANCE == null) {
-
synchronized (HttpManager.class) {
-
if (INSTANCE == null) {
-
INSTANCE = new HttpManager();
-
}
-
}
-
}
-
return INSTANCE;
-
}
2接口处理和回调处理:
-
/**
-
* 处理http请求
-
*
-
* @param basePar 封装的请求数据
-
*/
-
public void doHttpDeal(BaseApi basePar) {
-
//手动创建一个OkHttpClient并设置超时时间缓存等设置
-
OkHttpClient.Builder builder = new OkHttpClient.Builder();
-
builder.connectTimeout(basePar.getConnectionTime(), TimeUnit.SECONDS);
-
builder.addInterceptor( new CookieInterceptor(basePar.isCache()));
-
-
/*创建retrofit对象*/
-
Retrofit retrofit = new Retrofit.Builder()
-
.client(builder.build())
-
.addConverterFactory(GsonConverterFactory.create())
-
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
-
.baseUrl(basePar.getBaseUrl())
-
.build();
-
-
-
/*rx处理*/
-
ProgressSubscriber subscriber = new ProgressSubscriber(basePar);
-
Observable observable = basePar.getObservable(retrofit)
-
/*失败后的retry配置*/
-
.retryWhen( new RetryWhenNetworkException())
-
/*生命周期管理*/
-
.compose(basePar.getRxAppCompatActivity().bindToLifecycle())
-
/*http请求线程*/
-
.subscribeOn(Schedulers.io())
-
.unsubscribeOn(Schedulers.io())
-
/*回调线程*/
-
.observeOn(AndroidSchedulers.mainThread())
-
/*结果判断*/
-
.map(basePar);
-
-
/*数据回调*/
-
observable.subscribe(subscriber);
-
}
首先通过api
接口类BaseApi
的实现类中数据初始化OkHttpClient
和Retrofit
对象,其中包含了url
,超时等,接着通过BaseApi
的抽象方法getObservable
得到Observable
对象,得到Observable
对象以后,我们就能随意的切换现成来处理,整个请求通过compose
设定的rxlifecycle
来管理生命周期,所以不会溢出和泄露无需任何担心,最后再服务器数据返回时,通过map
判断结果,剔除错误信息,成功以后返回到自定义的ProgressSubscriber
对象中,所以接下来封装ProgressSubscriber
对象。
ProgressSubscriber
封装
ProgressSubscriber
其实是继承于Subscriber
,封装的方法无非是对Subscriber
的回调方法的封装
- onStart():开始
- onCompleted():结束
- onError(Throwable e):错误
- onNext(T t):成功
1.请求加载框
http
请求都伴随着加载框的使用,所以这里需要在onStart()
使用前初始一个加载框,这里简单的用ProgressDialog
代替
-
/**
-
* 用于在Http请求开始时,自动显示一个ProgressDialog
-
* 在Http请求结束是,关闭ProgressDialog
-
* 调用者自己对请求数据进行处理
-
* Created by WZG on 2016/7/16.
-
*/
-
public class ProgressSubscriber<T> extends Subscriber<T> {
-
/*是否弹框*/
-
private boolean showPorgress = true;
-
/* 软引用回调接口*/
-
private SoftReference<HttpOnNextListener> mSubscriberOnNextListener;
-
/*软引用反正内存泄露*/
-
private SoftReference<RxAppCompatActivity> mActivity;
-
/*加载框可自己定义*/
-
private ProgressDialog pd;
-
/*请求数据*/
-
private BaseApi api;
-
-
-
/**
-
* 构造
-
*
-
* @param api
-
*/
-
public ProgressSubscriber(BaseApi api) {
-
this.api = api;
-
this.mSubscriberOnNextListener = api.getListener();
-
this.mActivity = new SoftReference<>(api.getRxAppCompatActivity());
-
setShowPorgress(api.isShowProgress());
-
if (api.isShowProgress()) {
-
initProgressDialog(api.isCancel());
-
}
-
}
-
-
-
/**
-
* 初始化加载框
-
*/
-
private void initProgressDialog(boolean cancel) {
-
Context context = mActivity.get();
-
if (pd == null && context != null) {
-
pd = new ProgressDialog(context);
-
pd.setCancelable(cancel);
-
if (cancel) {
-
pd.setOnCancelListener( new DialogInterface.OnCancelListener() {
-
-
public void onCancel(DialogInterface dialogInterface) {
-
onCancelProgress();
-
}
-
});
-
}
-
}
-
}
-
-
-
/**
-
* 显示加载框
-
*/
-
private void showProgressDialog() {
-
if (!isShowPorgress()) return;
-
Context context = mActivity.get();
-
if (pd == null || context == null) return;
-
if (!pd.isShowing()) {
-
pd.show();
-
}
-
}
-
-
-
/**
-
* 隐藏
-
*/
-
private void dismissProgressDialog() {
-
if (!isShowPorgress()) return;
-
if (pd != null && pd.isShowing()) {
-
pd.dismiss();
-
}
-
}
-
}
由于progress
的特殊性,需要指定content
而且不能是Application
所以这里传递一个RxAppCompatActivity
,而同时上面的HttpManager
同样需要,所以这里统一还是按照BaseApi
传递过来,使用软引用的方式避免泄露。剩下的无非是初始化,显示和关闭方法,可以详细看代码。
2.onStart()
实现
在onStart()
中需要调用加载框,然后这里还有网络缓存的逻辑,后面会单独讲解,现在先忽略它的存在。
-
/**
-
* 订阅开始时调用
-
* 显示ProgressDialog
-
*/
-
-
public void onStart() {
-
showProgressDialog();
-
/*缓存并且有网*/
-
if (api.isCache() && AppUtil.isNetworkAvailable(RxRetrofitApp.getApplication())) {
-
/*获取缓存数据*/
-
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();
-
}
-
}
-
}
-
}
3.onCompleted()实现
-
/**
-
* 完成,隐藏ProgressDialog
-
*/
-
-
public void onCompleted() {
-
dismissProgressDialog();
-
}
4.onError(Throwable e)实现
在onError(Throwable e)
是对错误信息的处理和缓存读取的处理,后续会讲解,先忽略。
-
/**
-
* 对错误进行统一处理
-
* 隐藏ProgressDialog
-
*
-
* @param e
-
*/
-
-
public void onError(Throwable e) {
-
dismissProgressDialog();
-
/*需要緩存并且本地有缓存才返回*/
-
if (api.isCache()) {
-
Observable.just(api.getUrl()).subscribe( new Subscriber<String>() {
-
-
public void onCompleted() {
-
-
}
-
-
-
public void onError(Throwable e) {
-
errorDo(e);
-
}
-
-
-
public void onNext(String s) {
-
/*获取缓存数据*/
-
CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(s);
-
if (cookieResulte == null) {
-
throw new HttpTimeException( "网络错误");
-
}
-
long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
-
if (time < api.getCookieNoNetWorkTime()) {
-
if (mSubscriberOnNextListener.get() != null) {
-
mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
-
}
-
} else {
-
CookieDbUtil.getInstance().deleteCookie(cookieResulte);
-
throw new HttpTimeException( "网络错误");
-
}
-
}
-
});
-
} else {
-
errorDo(e);
-
}
-
}
-
-
/*错误统一处理*/
-
private void errorDo(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();
-
}
-
if (mSubscriberOnNextListener.get() != null) {
-
mSubscriberOnNextListener.get().onError(e);
-
}
-
}
5.onNext(T t)
实现
-
/**
-
* 将onNext方法中的返回结果交给Activity或Fragment自己处理
-
*
-
* @param t 创建Subscriber时的泛型类型
-
*/
-
-
public void onNext(T t) {
-
if (mSubscriberOnNextListener.get() != null) {
-
mSubscriberOnNextListener.get().onNext(t);
-
}
-
}
主要是是将得到的结果,通过自定义的接口返回给view
界面,其中的软引用对象mSubscriberOnNextListener
是自定义的接口回调类HttpOnNextListener
.
6.HttpOnNextListener
封装
现在只需关心onNext(T t)
和onError(Throwable e)
接口即可,回调的触发点都是在上面的ProgressSubscriber
中调用
-
/**
-
* 成功回调处理
-
* Created by WZG on 2016/7/16.
-
*/
-
public abstract class HttpOnNextListener<T> {
-
/**
-
* 成功后回调方法
-
* @param t
-
*/
-
public abstract void onNext(T t);
-
-
/**
-
* 緩存回調結果
-
* @param string
-
*/
-
public void onCacheNext(String string){
-
-
}
-
-
/**
-
* 失败或者错误方法
-
* 主动调用,更加灵活
-
* @param e
-
*/
-
public void onError(Throwable e){
-
-
}
-
-
/**
-
* 取消回調
-
*/
-
public void onCancel(){
-
-
}
-
}
失败后的retry
处理
这里你可能会问,Retrofit
有自带的retry
处理呀,的确Retrofit
有自带的retry
处理,但是有很多的局限,先看下使用
-
OkHttpClient.Builder builder = new OkHttpClient.Builder();
-
builder.retryOnConnectionFailure( true);
使用起来还是很方便,只需要调用一个方法即可,但是它是不可控的,也就是没有办法设置retry
时间次数,所以不太灵活,既然如此还不如自己封装一下,因为用RxJava
实现这个简直小菜,无形中好像已经给RxJava
打了广告,中毒太深。
很简单直接上代码:
-
/**
-
* retry条件
-
* Created by WZG on 2016/10/17.
-
*/
-
public class RetryWhenNetworkException implements Func1<Observable<? extends Throwable>, Observable<?>> {
-
// retry次数
-
private int count = 3;
-
// 延迟
-
private long delay = 3000;
-
// 叠加延迟
-
private long increaseDelay = 3000;
-
-
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;
-
}
-
-
-
public Observable<?> call(Observable<? extends Throwable> observable) {
-
return observable
-
.zipWith(Observable.range( 1, count + 1), new Func2<Throwable, Integer, Wrapper>() {
-
-
public Wrapper call(Throwable throwable, Integer integer) {
-
return new Wrapper(throwable, integer);
-
}
-
}).flatMap( new Func1<Wrapper, Observable<?>>() {
-
-
public Observable<?> call(Wrapper wrapper) {
-
if ((wrapper.throwable instanceof ConnectException
-
|| wrapper.throwable instanceof SocketTimeoutException
-
|| wrapper.throwable instanceof TimeoutException)
-
&& wrapper.index < count + 1) { //如果超出重试次数也抛出错误,否则默认是会进入onCompleted
-
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;
-
}
-
}
-
}
使用
到这里,我们第一步封装已经完成了,下面讲解下如何使用,已经看明白的各位看官,估计早就看明白了使用方式,无非是创建一个api
对象继承BaseApi
初始接口信息,然后调用HttpManager
对象的doHttpDeal(BaseApi basePar)
方法,最后静静的等待回调类HttpOnNextListener<T>
类返回的onNext(T t)
成功数据或者onError(Throwable e)
数据。
其实代码就是这样:
api
接口对象
-
/**
-
* 测试数据
-
* Created by WZG on 2016/7/16.
-
*/
-
public class SubjectPostApi extends BaseApi {
-
// 接口需要传入的参数 可自定义不同类型
-
private boolean all;
-
/*任何你先要传递的参数*/
-
// String xxxxx;
-
-
/**
-
* 默认初始化需要给定回调和rx周期类
-
* 可以额外设置请求设置加载框显示,回调等(可扩展)
-
* @param listener
-
* @param rxAppCompatActivity
-
*/
-
public SubjectPostApi(HttpOnNextListener listener, RxAppCompatActivity rxAppCompatActivity) {
-
super(listener,rxAppCompatActivity);
-
setShowProgress( true);
-
setCancel( true);
-
setCache( true);
-
setMothed( "AppFiftyToneGraph/videoLink");
-
setCookieNetWorkTime( 60);
-
setCookieNoNetWorkTime( 24* 60* 60);
-
}
-
-
public boolean isAll() {
-
return all;
-
}
-
-
public void setAll(boolean all) {
-
this.all = all;
-
}
-
-
-
public Observable getObservable(Retrofit retrofit) {
-
HttpPostService service = retrofit.create(HttpPostService.class);
-
return service.getAllVedioBys(isAll());
-
}
-
}
请求回调
-
-
// 完美封装简化版
-
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>>() {
-
-
public void onNext(List<SubjectResulte> subjects) {
-
tvMsg.setText( "网络返回:\n" + subjects.toString());
-
}
-
-
-
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() );
-
}
-
-
/*用户主动调用,默认是不需要覆写该方法*/
-
-
public void onError(Throwable e) {
-
super.onError(e);
-
tvMsg.setText( "失败:\n" + e.toString());
-
}
-
-
/*用户主动调用,默认是不需要覆写该方法*/
-
-
public void onCancel() {
-
super.onCancel();
-
tvMsg.setText( "取消請求");
-
}
-
};
后续
到这里,封装功能中很多功能还没涉及和讲解,后续会陆续更新!
先给大家看看为师的完全体功能:
-
1.Retrofit+Rxjava+okhttp基本使用方法
-
2.统一处理请求数据格式
-
3.统一的ProgressDialog和回调Subscriber处理
-
4.取消http请求
-
5.预处理http请求
-
6.返回数据的统一判断
-
7.失败后的retry处理
-
8.RxLifecycle管理生命周期,防止泄露
-
9.文件上传下载(支持多文件,断点续传)
-
10.Cache数据持久化和数据库(greenDao)两种缓存机制
-
11.异常统一处理
来个图压压惊:
迫不及待的小伙伴可以看这里:
但是其中有些后续优化迭代未及时更新,别生气,我马上补!
源码:
建议
CSDN
博客发布了一系列的RxJava+Retrofit+OkHttp
深入浅出-终极封装 之前发出后收到很多朋友的关注,原本只是自己学习后的一些经验总结,但是有同学运用到实战当中,这让我很惶恐,所有后续一直更新了很多次版本,有些地方难免有所变动导致之前的博客有所出入,正好最近受到掘金邀请内测博客,所以决定重新写一版,按照最后迭代完成的封装详细的讲述一遍,欢迎大家关注!
注意:由于本章的特殊性,后续文章比较长而且复杂,涉及内容也很多,所以大家准备好茶水,前方高能预警。
封装成果
封装完以后,具有如下功能:
-
1.Retrofit+Rxjava+okhttp基本使用方法
-
2.统一处理请求数据格式
-
3.统一的ProgressDialog和回调Subscriber处理
-
4.取消http请求
-
5.预处理http请求
-
6.返回数据的统一判断
-
7.失败后的retry封装处理
-
8.RxLifecycle管理生命周期,防止泄露
实现效果:
具体使用
封装后http请求代码如下
-
// 完美封装简化版
-
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>>() {
-
-
public void onNext(List<Subject> subjects) {
-
tvMsg.setText( "已封装:\n" + subjects.toString());
-
}
-
-
/*用户主动调用,默认是不需要覆写该方法*/
-
-
public void onError(Throwable e) {
-
super.onError(e);
-
tvMsg.setText( "失败:\n" + e.toString());
-
}
-
};
是不是很简单?你可能说这还简单,好咱们对比一下正常使用Retrofit
的方法
-
/**
-
* 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>() {
-
-
public void onCompleted() {
-
if (pd != null && pd.isShowing()) {
-
pd.dismiss();
-
}
-
}
-
-
-
public void onError(Throwable e) {
-
if (pd != null && pd.isShowing()) {
-
pd.dismiss();
-
}
-
}
-
-
-
public void onNext(RetrofitEntity retrofitEntity) {
-
tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
-
}
-
-
-
public void onStart() {
-
super.onStart();
-
pd.show();
-
}
-
}
-
-
);
-
}
可能你发现确是代码有点多,但是更加可怕的是,如果你一个activity或者fragment中多次需要http请求,你需要多次重复的写回调处理(一个回到就有4个方法呀!!!!反正我是忍受不了),而且以上处理还没有做过多的判断和错误校验就如此复杂!~好了介绍完了,开始咱们的优化之路吧!
项目结构:
RxJava
如果你对RxJava
不了解,好吧骚年赶快学学吧,不然真会out
了,下面给出博主当初学习RxJava
的一些资源:
Retrofit
咱家今天的主角来了,咱们也深入浅出一下了解下Retrofit
使用,前方高能,如果你是深度Retrofit
选手请直接跳过本节!!!
1.首先确保在AndroidManifest.xml中请求了网络权限
<uses-permission android:name="android.permission.INTERNET"/>
2.在app/build.gradle添加引用
-
/*rx-android-java*/
-
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
-
compile 'com.trello:rxlifecycle:1.0'
-
compile 'com.trello:rxlifecycle-components:1.0'
-
/*rotrofit*/
-
compile 'com.squareup.retrofit2:retrofit:2.1.0'
-
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
-
compile 'com.google.code.gson:gson:2.8.0'
3.常用注解
这里介绍一些常用的注解的使用
@Query
、@QueryMap
:用于Http Get
请求传递参数@Field
:用于Post
方式传递参数,需要在请求接口方法上添加@FormUrlEncoded
,即以表单的方式传递参数@Body
:用于Post
,根据转换方式将实例对象转化为对应字符串传递参数.比如Retrofit
添加GsonConverterFactory
则是将body
转化为gson
字符串进行传递@Path
:用于URL
上占位符@Part
:配合@Multipart
使用,一般用于文件上传@Header
:添加http header
@Headers
:跟@Header
作用一样,只是使用方式不一样,@Header
是作为请求方法的参数传入,@Headers
是以固定方式直接添加到请求方法上
ReTrofit
基本使用:
首先给定一个测试接口文档,后面的博客中我们都是用这个接口调试
-
/**
-
* @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
(通常情况下就是域名)。
-
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
, 等等。) 方法的注解以及用于分发网络调用的方法。而且这些方法的参数也可以有特殊的注解。
-
/**
-
* 接口地址
-
* Created by WZG on 2016/7/16.
-
*/
-
public interface MyApiEndpointInterface {
-
"AppFiftyToneGraph/videoLink")(
-
Call<RetrofitEntity> getAllVedio(@Body boolean once_no)
-
}
3.得到call
然后同步处理处理回调:
-
MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class);
-
Call<RetrofitEntity> call = apiService.getAllVedio( true);
-
call.enqueue( new Callback<RetrofitEntity>() {
-
-
public void onResponse(Response<RetrofitEntity> response, Retrofit retrofit) {
-
RetrofitEntity entity = response.body();
-
Log.i( "tag", "onResponse----->" + entity.getMsg());
-
}
-
-
-
public void onFailure(Throwable t) {
-
Log.i( "tag", "onFailure----->" + t.toString());
-
-
}
-
});
这就是简单的Retrofit
使用步骤,接下来我们结合RxJava讲述
ReTrofit+Rxjava
基本使用
对比之前的Retrofit
使用
1.在于我们需要修改service
接口返回信息我们需要返回一个Observable
对象
-
"AppFiftyToneGraph/videoLink")(
-
Observable<RetrofitEntity> getAllVedioBy(@Body boolean once_no);
2.然后初始化Retrofit
需要添加对Rxjava
的适配,注意一定要retrofit2
才有这个功能哦
-
Retrofit retrofit = new Retrofit.Builder()
-
.client(builder.build())
-
.addConverterFactory(GsonConverterFactory.create())
-
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
-
.baseUrl(HttpManager.BASE_URL)
-
.build();
3.回调通过RxJava
处理
-
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>() {
-
-
public void onCompleted() {
-
}
-
-
-
public void onError(Throwable e) {
-
}
-
-
-
public void onNext(RetrofitEntity retrofitEntity) {
-
tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
-
}
-
}
-
-
);
简单的RxJava
集合Retrofit
的使用就介绍完了,同样的可以发现使用起来很多重复性的代码,而且使用也不是那么简单,所以才有了下面的封装
ReTrofit+Rxjava进阶封装之路
先来一张流程图压压惊
请求数据封装
1.参数
首先需要封装的使我们的数据类,在数据类中需要封装请求中用到的相关数据的设置,比如请求参数、方法、加载框显示设置等等
-
public abstract class BaseApi<T> implements Func1<BaseResultEntity<T>, T> {
-
//rx生命周期管理
-
private SoftReference<RxAppCompatActivity> rxAppCompatActivity;
-
/*回调*/
-
private SoftReference<HttpOnNextListener> listener;
-
/*是否能取消加载框*/
-
private boolean cancel;
-
/*是否显示加载框*/
-
private boolean showProgress;
-
/*是否需要缓存处理*/
-
private boolean cache;
-
/*基础url*/
-
private String baseUrl= "http://www.izaodao.com/Api/";
-
/*方法-如果需要缓存必须设置这个参数;不需要不用設置*/
-
private String mothed;
-
/*超时时间-默认6秒*/
-
private int connectionTime = 6;
-
/*有网情况下的本地缓存时间默认60秒*/
-
private int cookieNetWorkTime= 60;
-
/*无网络的情况下本地缓存时间默认30天*/
-
private int cookieNoNetWorkTime= 24* 60* 60* 30;
-
}
注释很详细,这里不具体描述了,由于这里是最后封装完成以后的代码,所以有些内容本章还会部分不会涉及,因为功能太多,还是按照一开始的博客章节讲解。
2.抽象api
接口
-
/**
-
* 设置参数
-
*
-
* @param retrofit
-
* @return
-
*/
-
public abstract Observable getObservable(Retrofit retrofit);
通过子类也即是我们的具体api
接口,通过getObservable
实现service
中定义的接口方法,例如:
-
public class SubjectPostApi extends BaseApi {
-
xxxxxxx
-
xxxxxxx
-
-
-
public Observable getObservable(Retrofit retrofit) {
-
HttpPostService service = retrofit.create(HttpPostService.class);
-
return service.getAllVedioBys(isAll());
-
}
-
}
通过传入的Retrofit
对象,可以随意切换挑选Service
对象,得到定义的注解方法,初始完成以后返回Observable
对象。
3.结果判断
这里结合RxJava
的map
方法在服务器返回数据中,统一处理数据处理,所以BaseApi<T> implements Func1<BaseResultEntity<T>, T>
,后边结合结果处理链接起来使用
-
-
public T call(BaseResultEntity<T> httpResult) {
-
if (httpResult.getRet() == 0) {
-
throw new HttpTimeException(httpResult.getMsg());
-
}
-
return httpResult.getData();
-
}
由于测试接口,也是当前我们公司接口都是有统一规则的,想必大家都有这样的接口规则,所以才有这里的统一判断,规则如下:
-
* ret: 1成功, 2失败
-
* msg:信息
-
* data:{
-
* name:视频名称
-
* title:标题
-
* }
其实上面的接口文档中就介绍了,统一先通过ret
判断,失败显示msg
信息,data
是成功后的数据也就是用户关心的数据,所以可封装一个结果对象BaseResultEntity
.
4.结果数据
-
/**
-
* 回调信息统一封装类
-
* Created by WZG on 2016/7/16.
-
*/
-
public class BaseResultEntity<T> {
-
// 判断标示
-
private int ret;
-
// 提示信息
-
private String msg;
-
//显示数据(用户需要关心的数据)
-
private T data;
-
-
-
xxxxx get-set xxxxx
-
}
这里结合BaseApi
的Func1
判断,失败直接抛出一个异常,交个RxJava
的onError
处理,成功则将用户关心的数据传给Gson
解析返回
5.泛型传递
BaseResultEntity<T>
中的泛型T
也就是我们所关心的回调数据,同样也是Gson最后解析返回的数据,传递的过程根节点是通过定义service
方法是给定的,例如:
-
public interface HttpPostService {
-
"AppFiftyToneGraph/videoLink")(
-
Call<RetrofitEntity> getAllVedio(@Body boolean once_no);
-
}
其中的RetrofitEntity
就是用户关心的数据类,通过泛型传递给最后的接口。
6.强调
很多兄弟通过QQ群反馈给我说,使用一个接口需要写一个对应的api
类继承BaseApi
是不是很麻烦,我这里强调一下,这样封装是为了将一个Api
接口作为一个对象去封装,个人觉得有必要封装成一个类,在日后工程日益增加接口随着增加的同时,对象的做法更加有利于查找接口和修改接口有利于迭代。
操作类封装
1初始对象
首先初始化一个单利方便HttpManager
请求;这里用了volatile
的对象,不懂的同学可以参考我的另一篇博客
-
private volatile static HttpManager INSTANCE;
-
-
//构造方法私有
-
private HttpManager() {
-
}
-
-
//获取单例
-
public static HttpManager getInstance() {
-
if (INSTANCE == null) {
-
synchronized (HttpManager.class) {
-
if (INSTANCE == null) {
-
INSTANCE = new HttpManager();
-
}
-
}
-
}
-
return INSTANCE;
-
}
2接口处理和回调处理:
-
/**
-
* 处理http请求
-
*
-
* @param basePar 封装的请求数据
-
*/
-
public void doHttpDeal(BaseApi basePar) {
-
//手动创建一个OkHttpClient并设置超时时间缓存等设置
-
OkHttpClient.Builder builder = new OkHttpClient.Builder();
-
builder.connectTimeout(basePar.getConnectionTime(), TimeUnit.SECONDS);
-
builder.addInterceptor( new CookieInterceptor(basePar.isCache()));
-
-
/*创建retrofit对象*/
-
Retrofit retrofit = new Retrofit.Builder()
-
.client(builder.build())
-
.addConverterFactory(GsonConverterFactory.create())
-
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
-
.baseUrl(basePar.getBaseUrl())
-
.build();
-
-
-
/*rx处理*/
-
ProgressSubscriber subscriber = new ProgressSubscriber(basePar);
-
Observable observable = basePar.getObservable(retrofit)
-
/*失败后的retry配置*/
-
.retryWhen( new RetryWhenNetworkException())
-
/*生命周期管理*/
-
.compose(basePar.getRxAppCompatActivity().bindToLifecycle())
-
/*http请求线程*/
-
.subscribeOn(Schedulers.io())
-
.unsubscribeOn(Schedulers.io())
-
/*回调线程*/
-
.observeOn(AndroidSchedulers.mainThread())
-
/*结果判断*/
-
.map(basePar);
-
-
/*数据回调*/
-
observable.subscribe(subscriber);
-
}
首先通过api
接口类BaseApi
的实现类中数据初始化OkHttpClient
和Retrofit
对象,其中包含了url
,超时等,接着通过BaseApi
的抽象方法getObservable
得到Observable
对象,得到Observable
对象以后,我们就能随意的切换现成来处理,整个请求通过compose
设定的rxlifecycle
来管理生命周期,所以不会溢出和泄露无需任何担心,最后再服务器数据返回时,通过map
判断结果,剔除错误信息,成功以后返回到自定义的ProgressSubscriber
对象中,所以接下来封装ProgressSubscriber
对象。
ProgressSubscriber
封装
ProgressSubscriber
其实是继承于Subscriber
,封装的方法无非是对Subscriber
的回调方法的封装
- onStart():开始
- onCompleted():结束
- onError(Throwable e):错误
- onNext(T t):成功
1.请求加载框
http
请求都伴随着加载框的使用,所以这里需要在onStart()
使用前初始一个加载框,这里简单的用ProgressDialog
代替
-
/**
-
* 用于在Http请求开始时,自动显示一个ProgressDialog
-
* 在Http请求结束是,关闭ProgressDialog
-
* 调用者自己对请求数据进行处理
-
* Created by WZG on 2016/7/16.
-
*/
-
public class ProgressSubscriber<T> extends Subscriber<T> {
-
/*是否弹框*/
-
private boolean showPorgress = true;
-
/* 软引用回调接口*/
-
private SoftReference<HttpOnNextListener> mSubscriberOnNextListener;
-
/*软引用反正内存泄露*/
-
private SoftReference<RxAppCompatActivity> mActivity;
-
/*加载框可自己定义*/
-
private ProgressDialog pd;
-
/*请求数据*/
-
private BaseApi api;
-
-
-
/**
-
* 构造
-
*
-
* @param api
-
*/
-
public ProgressSubscriber(BaseApi api) {
-
this.api = api;
-
this.mSubscriberOnNextListener = api.getListener();
-
this.mActivity = new SoftReference<>(api.getRxAppCompatActivity());
-
setShowPorgress(api.isShowProgress());
-
if (api.isShowProgress()) {
-
initProgressDialog(api.isCancel());
-
}
-
}
-
-
-
/**
-
* 初始化加载框
-
*/
-
private void initProgressDialog(boolean cancel) {
-
Context context = mActivity.get();
-
if (pd == null && context != null) {
-
pd = new ProgressDialog(context);
-
pd.setCancelable(cancel);
-
if (cancel) {
-
pd.setOnCancelListener( new DialogInterface.OnCancelListener() {
-
-
public void onCancel(DialogInterface dialogInterface) {
-
onCancelProgress();
-
}
-
});
-
}
-
}
-
}
-
-
-
/**
-
* 显示加载框
-
*/
-
private void showProgressDialog() {
-
if (!isShowPorgress()) return;
-
Context context = mActivity.get();
-
if (pd == null || context == null) return;
-
if (!pd.isShowing()) {
-
pd.show();
-
}
-
}
-
-
-
/**
-
* 隐藏
-
*/
-
private void dismissProgressDialog() {
-
if (!isShowPorgress()) return;
-
if (pd != null && pd.isShowing()) {
-
pd.dismiss();
-
}
-
}
-
}
由于progress
的特殊性,需要指定content
而且不能是Application
所以这里传递一个RxAppCompatActivity
,而同时上面的HttpManager
同样需要,所以这里统一还是按照BaseApi
传递过来,使用软引用的方式避免泄露。剩下的无非是初始化,显示和关闭方法,可以详细看代码。
2.onStart()
实现
在onStart()
中需要调用加载框,然后这里还有网络缓存的逻辑,后面会单独讲解,现在先忽略它的存在。
-
/**
-
* 订阅开始时调用
-
* 显示ProgressDialog
-
*/
-
-
public void onStart() {
-
showProgressDialog();
-
/*缓存并且有网*/
-
if (api.isCache() && AppUtil.isNetworkAvailable(RxRetrofitApp.getApplication())) {
-
/*获取缓存数据*/
-
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();
-
}
-
}
-
}
-
}
3.onCompleted()实现
-
/**
-
* 完成,隐藏ProgressDialog
-
*/
-
-
public void onCompleted() {
-
dismissProgressDialog();
-
}
4.onError(Throwable e)实现
在onError(Throwable e)
是对错误信息的处理和缓存读取的处理,后续会讲解,先忽略。
-
/**
-
* 对错误进行统一处理
-
* 隐藏ProgressDialog
-
*
-
* @param e
-
*/
-
-
public void onError(Throwable e) {
-
dismissProgressDialog();
-
/*需要緩存并且本地有缓存才返回*/
-
if (api.isCache()) {
-
Observable.just(api.getUrl()).subscribe( new Subscriber<String>() {
-
-
public void onCompleted() {
-
-
}
-
-
-
public void onError(Throwable e) {
-
errorDo(e);
-
}
-
-
-
public void onNext(String s) {
-
/*获取缓存数据*/
-
CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(s);
-
if (cookieResulte == null) {
-
throw new HttpTimeException( "网络错误");
-
}
-
long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
-
if (time < api.getCookieNoNetWorkTime()) {
-
if (mSubscriberOnNextListener.get() != null) {
-
mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
-
}
-
} else {
-
CookieDbUtil.getInstance().deleteCookie(cookieResulte);
-
throw new HttpTimeException( "网络错误");
-
}
-
}
-
});
-
} else {
-
errorDo(e);
-
}
-
}
-
-
/*错误统一处理*/
-
private void errorDo(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();
-
}
-
if (mSubscriberOnNextListener.get() != null) {
-
mSubscriberOnNextListener.get().onError(e);
-
}
-
}
5.onNext(T t)
实现
-
/**
-
* 将onNext方法中的返回结果交给Activity或Fragment自己处理
-
*
-
* @param t 创建Subscriber时的泛型类型
-
*/
-
-
public void onNext(T t) {
-
if (mSubscriberOnNextListener.get() != null) {
-
mSubscriberOnNextListener.get().onNext(t);
-
}
-
}
主要是是将得到的结果,通过自定义的接口返回给view
界面,其中的软引用对象mSubscriberOnNextListener
是自定义的接口回调类HttpOnNextListener
.
6.HttpOnNextListener
封装
现在只需关心onNext(T t)
和onError(Throwable e)
接口即可,回调的触发点都是在上面的ProgressSubscriber
中调用
-
/**
-
* 成功回调处理
-
* Created by WZG on 2016/7/16.
-
*/
-
public abstract class HttpOnNextListener<T> {
-
/**
-
* 成功后回调方法
-
* @param t
-
*/
-
public abstract void onNext(T t);
-
-
/**
-
* 緩存回調結果
-
* @param string
-
*/
-
public void onCacheNext(String string){
-
-
}
-
-
/**
-
* 失败或者错误方法
-
* 主动调用,更加灵活
-
* @param e
-
*/
-
public void onError(Throwable e){
-
-
}
-
-
/**
-
* 取消回調
-
*/
-
public void onCancel(){
-
-
}
-
}
失败后的retry
处理
这里你可能会问,Retrofit
有自带的retry
处理呀,的确Retrofit
有自带的retry
处理,但是有很多的局限,先看下使用
-
OkHttpClient.Builder builder = new OkHttpClient.Builder();
-
builder.retryOnConnectionFailure( true);
使用起来还是很方便,只需要调用一个方法即可,但是它是不可控的,也就是没有办法设置retry
时间次数,所以不太灵活,既然如此还不如自己封装一下,因为用RxJava
实现这个简直小菜,无形中好像已经给RxJava
打了广告,中毒太深。
很简单直接上代码:
-
/**
-
* retry条件
-
* Created by WZG on 2016/10/17.
-
*/
-
public class RetryWhenNetworkException implements Func1<Observable<? extends Throwable>, Observable<?>> {
-
// retry次数
-
private int count = 3;
-
// 延迟
-
private long delay = 3000;
-
// 叠加延迟
-
private long increaseDelay = 3000;
-
-
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;
-
}
-
-
-
public Observable<?> call(Observable<? extends Throwable> observable) {
-
return observable
-
.zipWith(Observable.range( 1, count + 1), new Func2<Throwable, Integer, Wrapper>() {
-
-
public Wrapper call(Throwable throwable, Integer integer) {
-
return new Wrapper(throwable, integer);
-
}
-
}).flatMap( new Func1<Wrapper, Observable<?>>() {
-
-
public Observable<?> call(Wrapper wrapper) {
-
if ((wrapper.throwable instanceof ConnectException
-
|| wrapper.throwable instanceof SocketTimeoutException
-
|| wrapper.throwable instanceof TimeoutException)
-
&& wrapper.index < count + 1) { //如果超出重试次数也抛出错误,否则默认是会进入onCompleted
-
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;
-
}
-
}
-
}
使用
到这里,我们第一步封装已经完成了,下面讲解下如何使用,已经看明白的各位看官,估计早就看明白了使用方式,无非是创建一个api
对象继承BaseApi
初始接口信息,然后调用HttpManager
对象的doHttpDeal(BaseApi basePar)
方法,最后静静的等待回调类HttpOnNextListener<T>
类返回的onNext(T t)
成功数据或者onError(Throwable e)
数据。
其实代码就是这样:
api
接口对象
-
/**
-
* 测试数据
-
* Created by WZG on 2016/7/16.
-
*/
-
public class SubjectPostApi extends BaseApi {
-
// 接口需要传入的参数 可自定义不同类型
-
private boolean all;
-
/*任何你先要传递的参数*/
-
// String xxxxx;
-
-
/**
-
* 默认初始化需要给定回调和rx周期类
-
* 可以额外设置请求设置加载框显示,回调等(可扩展)
-
* @param listener
-
* @param rxAppCompatActivity
-
*/
-
public SubjectPostApi(HttpOnNextListener listener, RxAppCompatActivity rxAppCompatActivity) {
-
super(listener,rxAppCompatActivity);
-
setShowProgress( true);
-
setCancel( true);
-
setCache( true);
-
setMothed( "AppFiftyToneGraph/videoLink");
-
setCookieNetWorkTime( 60);
-
setCookieNoNetWorkTime( 24* 60* 60);
-
}
-
-
public boolean isAll() {
-
return all;
-
}
-
-
public void setAll(boolean all) {
-
this.all = all;
-
}
-
-
-
public Observable getObservable(Retrofit retrofit) {
-
HttpPostService service = retrofit.create(HttpPostService.class);
-
return service.getAllVedioBys(isAll());
-
}
-
}
请求回调
-
-
// 完美封装简化版
-
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>>() {
-
-
public void onNext(List<SubjectResulte> subjects) {
-
tvMsg.setText( "网络返回:\n" + subjects.toString());
-
}
-
-
-
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() );
-
}
-
-
/*用户主动调用,默认是不需要覆写该方法*/
-
-
public void onError(Throwable e) {
-
super.onError(e);
-
tvMsg.setText( "失败:\n" + e.toString());
-
}
-
-
/*用户主动调用,默认是不需要覆写该方法*/
-
-
public void onCancel() {
-
super.onCancel();
-
tvMsg.setText( "取消請求");
-
}
-
};
后续
到这里,封装功能中很多功能还没涉及和讲解,后续会陆续更新!
先给大家看看为师的完全体功能:
-
1.Retrofit+Rxjava+okhttp基本使用方法
-
2.统一处理请求数据格式
-
3.统一的ProgressDialog和回调Subscriber处理
-
4.取消http请求
-
5.预处理http请求
-
6.返回数据的统一判断
-
7.失败后的retry处理
-
8.RxLifecycle管理生命周期,防止泄露
-
9.文件上传下载(支持多文件,断点续传)
-
10.Cache数据持久化和数据库(greenDao)两种缓存机制
-
11.异常统一处理
来个图压压惊:
迫不及待的小伙伴可以看这里:
但是其中有些后续优化迭代未及时更新,别生气,我马上补!