Rxjava+ReTrofit+okHttp深入浅出-终极封装
背景:
学习Rxjava和retrofit已经很长时间了,功能确实很强大,但是使用起来还是有点复杂,代码的重复性太高,所以决定把基于retrofit和rxjava的处理统一封装起来,实现的功能:
1.Retrofit+Rxjava+okhttp基本使用方法
2.统一处理请求数据格式
3.统一的ProgressDialog和回调Subscriber处理
4.取消http请求
5.预处理http请求
5.返回数据的统一判断
效果:
封装后http请求代码如下
// 完美封装简化版 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()); } };>
是不是很简单?你可能说这还简单,好咱们对比一下正常使用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>() { @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
进阶:
ReTrofit基本设置
咱家今天的主角来了,咱们也深入浅出一下,前方高能,如果你是深度retrofit选手请直接跳过本节!!!
1.首先确保在AndroidManifest.xml中请求了网络权限
<uses-permission android:name="android.permission.INTERNET"/>
2.在app/build.gradle添加引用
/*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基本使用:
后面的文章中我们都是用这个接口调试
/** * @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 { @POST("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>() { @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对象
@POST("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>() { @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的对象,不懂的同学可以参考我的另一篇博客
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对象
/** * 处理http请求 * * @param basePar 封装的请求数据 */ public void doHttpDeal(BaseEntity basePar) { Observable observable = basePar.getObservable(httpService) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map(basePar); observable.subscribe(basePar.getSubscirber()); }
可以发现主要的封装都放入了BaseEntity中,我们跟着思路进入到BaseEntity
2.数据统一请求参数和数据封装BaseEntity
2.1:getObservable通过传入一个**service对象获取需要处理的Observable对象
2.2:getSubScirber得到一个回调sub对象
这两个方法都需要其子类实现覆盖
2.3:在base里面我们继承Fucn1方法通过泛型去处理服务器返回数据判断(这时候我们可以在回调信息前提前处理错误信息),可以自己封装信息哦!如果是有效数据才返回给next方法,错误信息或者无数据等直接抛出一个自己定义的异常信息在onerror方法中处理!
/** * 请求数据统一封装类 * 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();
@Override public T call(BaseResultEntity<T> httpResult) { if (httpResult.getRet() == 0) { throw new HttpTimeException(httpResult.getMsg()); } return httpResult.getData(); } }
3自定义异常处理
/** * 自定义错误信息,统一处理返回处理 * 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.BaseEntity集成覆盖主要方法,实现处理的和返回的一一绑定
/** * 测试接口请求数据 * 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; } }
5.然后定义service接口按照泛型的方式传递返回接口的数据
@POST("AppFiftyToneGraph/videoLink") Observable<BaseResultEntity<List<Subject>>> getAllVedioBys(@Body boolean once_no);
6.定义返回数据的基础类,统一处理结果信息:
/** * 回调信息统一封装类 * Created by WZG on 2016/7/16. */ public class BaseResultEntity<T> { // 判断标示 private int ret; // 提示信息 private String msg; //显示数据(用户需要关心的数据) private T data;}
7.封装回调的subservice统一处理加载框,错误提示,结果回调等处理
我们在subserice的四个回调方法中,统一处理onstart中我们现实加载框,onerror中统一处理系统错误和自定义错误信息,我们可以在baseentity的 funct1方法中统一处理返回信息,比如咱们测试方法我们可以统一判断ret参数,如果返回的是2我们抛出一个自定义的错误;或者返回的array数据为空我们也可以统一抛出一个空数据的异常!在oonext方法中我们返回一个泛型接口,传出最后service接口指定的泛型的对象!
/** * 用于在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(); } } }
8.自定义返回接口,泛型传递返回值
/** * 成功回调处理 * Created by WZG on 2016/7/16. */ public interface HttpOnNextListener<T> { void onNext(T t); }
9.发起http请求处理
定义一个指定类型的回调类httpOnextListener,然后初始化一个统一处理返回结果的ProgressSubscriber对象,将sub传入给封装的BaseEntity对象中,发起网络请求,通过Rxjava后台处理网络请求,主线程处理返回结果;网络请求onext前,调用BaseEntity的fun1方法,统一判断返回信息(可预处理错误),最后通过自定义泛型httpOnextListener返回成功数据
// 完美封装简化版 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()); } };