RxJava+Retrofit+OkHttp深入浅出-终极封装二(网络请求) RxJava+Retrofit+OkHttp深入浅出-终极封装二(网络请求) RxJava+Retrofit+OkHttp深入浅出-终极封装二(网络请求)



RxJava+Retrofit+OkHttp深入浅出-终极封装二(网络请求)

背景:

CSDN博客发布了一系列的RxJava+Retrofit+OkHttp深入浅出-终极封装 之前发出后收到很多朋友的关注,原本只是自己学习后的一些经验总结,但是有同学运用到实战当中,这让我很惶恐,所有后续一直更新了很多次版本,有些地方难免有所变动导致之前的博客有所出入,正好最近受到掘金邀请内测博客,所以决定重新写一版,按照最后迭代完成的封装详细的讲述一遍,欢迎大家关注!

注意:由于本章的特殊性,后续文章比较长而且复杂,涉及内容也很多,所以大家准备好茶水,前方高能预警。


RxJava+Retrofit+OkHttp深入浅出-终极封装


封装成果

封装完以后,具有如下功能:

  1. 1.Retrofit+Rxjava+okhttp基本使用方法
  2. 2.统一处理请求数据格式
  3. 3.统一的ProgressDialog和回调Subscriber处理
  4. 4.取消http请求
  5. 5.预处理http请求
  6. 6.返回数据的统一判断
  7. 7.失败后的retry封装处理
  8. 8.RxLifecycle管理生命周期,防止泄露

实现效果:


具体使用

封装后http请求代码如下

  1. // 完美封装简化版
  2. private void simpleDo() {
  3. SubjectPost postEntity = new SubjectPost(simpleOnNextListener, this);
  4. postEntity.setAll( true);
  5. HttpManager manager = HttpManager.getInstance();
  6. manager.doHttpDeal(postEntity);
  7. }
  8. // 回调一一对应
  9. HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<Subject>>() {
  10. @Override
  11. public void onNext(List<Subject> subjects) {
  12. tvMsg.setText( "已封装:\n" + subjects.toString());
  13. }
  14. /*用户主动调用,默认是不需要覆写该方法*/
  15. @Override
  16. public void onError(Throwable e) {
  17. super.onError(e);
  18. tvMsg.setText( "失败:\n" + e.toString());
  19. }
  20. };


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


  1. /**
  2. * Retrofit加入rxjava实现http请求
  3. */
  4. private void onButton9Click() {
  5. //手动创建一个OkHttpClient并设置超时时间
  6. okhttp3.OkHttpClient.Builder builder = new OkHttpClient.Builder();
  7. builder.connectTimeout( 5, TimeUnit.SECONDS);
  8. Retrofit retrofit = new Retrofit.Builder()
  9. .client(builder.build())
  10. .addConverterFactory(GsonConverterFactory.create())
  11. .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  12. .baseUrl(HttpManager.BASE_URL)
  13. .build();
  14. / 加载框
  15. final ProgressDialog pd = new ProgressDialog( this);
  16. HttpService apiService = retrofit.create(HttpService.class);
  17. Observable<RetrofitEntity> observable = apiService.getAllVedioBy( true);
  18. observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
  19. .subscribe(
  20. new Subscriber<RetrofitEntity>() {
  21. @Override
  22. public void onCompleted() {
  23. if (pd != null && pd.isShowing()) {
  24. pd.dismiss();
  25. }
  26. }
  27. @Override
  28. public void onError(Throwable e) {
  29. if (pd != null && pd.isShowing()) {
  30. pd.dismiss();
  31. }
  32. }
  33. @Override
  34. public void onNext(RetrofitEntity retrofitEntity) {
  35. tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
  36. }
  37. @Override
  38. public void onStart() {
  39. super.onStart();
  40. pd.show();
  41. }
  42. }
  43. );
  44. }


可能你发现确是代码有点多,但是更加可怕的是,如果你一个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添加引用

  1. /*rx-android-java*/
  2. compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
  3. compile 'com.trello:rxlifecycle:1.0'
  4. compile 'com.trello:rxlifecycle-components:1.0'
  5. /*rotrofit*/
  6. compile 'com.squareup.retrofit2:retrofit:2.1.0'
  7. compile 'com.squareup.retrofit2:converter-gson:2.0.0'
  8. 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基本使用:

首先给定一个测试接口文档,后面的博客中我们都是用这个接口调试

  1. /**
  2. * @api videoLink 50音图视频链接
  3. * @url http://www.izaodao.com/Api/AppFiftyToneGraph/videoLink
  4. * @method post
  5. * @param once_no bool(选填,ture无链接) 一次性获取下载地址
  6. * @return json array(
  7. * ret:1成功,2失败
  8. * msg:信息
  9. * data:{
  10. * name:视频名称
  11. * title:标题
  12. * }
  13. )

1.初始化retrofit

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

  1. String BASE_URL = " http://www.izaodao.com/Api/"
  2. Retrofit retrofit = new Retrofit.Builder()
  3. .baseUrl(BASE_URL)
  4. .addConverterFactory(GsonConverterFactory.create())
  5. .build();


2.设置接口service

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

  1. /**
  2. * 接口地址
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public interface MyApiEndpointInterface {
  6. @POST( "AppFiftyToneGraph/videoLink")
  7. Call<RetrofitEntity> getAllVedio(@Body boolean once_no)
  8. }


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

  1. MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class);
  2. Call<RetrofitEntity> call = apiService.getAllVedio( true);
  3. call.enqueue( new Callback<RetrofitEntity>() {
  4. @Override
  5. public void onResponse(Response<RetrofitEntity> response, Retrofit retrofit) {
  6. RetrofitEntity entity = response.body();
  7. Log.i( "tag", "onResponse----->" + entity.getMsg());
  8. }
  9. @Override
  10. public void onFailure(Throwable t) {
  11. Log.i( "tag", "onFailure----->" + t.toString());
  12. }
  13. });

这就是简单的Retrofit使用步骤,接下来我们结合RxJava讲述


ReTrofit+Rxjava基本使用

对比之前的Retrofit使用

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

  1. @POST( "AppFiftyToneGraph/videoLink")
  2. Observable<RetrofitEntity> getAllVedioBy(@Body boolean once_no);

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

  1. Retrofit retrofit = new Retrofit.Builder()
  2. .client(builder.build())
  3. .addConverterFactory(GsonConverterFactory.create())
  4. .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  5. .baseUrl(HttpManager.BASE_URL)
  6. .build();

3.回调通过RxJava处理

  1. HttpService apiService = retrofit.create(HttpService.class);
  2. Observable<RetrofitEntity> observable = apiService.getAllVedioBy( true);
  3. observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
  4. .subscribe(
  5. new Subscriber<RetrofitEntity>() {
  6. @Override
  7. public void onCompleted() {
  8. }
  9. @Override
  10. public void onError(Throwable e) {
  11. }
  12. @Override
  13. public void onNext(RetrofitEntity retrofitEntity) {
  14. tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
  15. }
  16. }
  17. );

简单的RxJava集合Retrofit的使用就介绍完了,同样的可以发现使用起来很多重复性的代码,而且使用也不是那么简单,所以才有了下面的封装


ReTrofit+Rxjava进阶封装之路

先来一张流程图压压惊


请求数据封装

1.参数

首先需要封装的使我们的数据类,在数据类中需要封装请求中用到的相关数据的设置,比如请求参数、方法、加载框显示设置等等

  1. public abstract class BaseApi<T> implements Func1<BaseResultEntity<T>, T> {
  2. //rx生命周期管理
  3. private SoftReference<RxAppCompatActivity> rxAppCompatActivity;
  4. /*回调*/
  5. private SoftReference<HttpOnNextListener> listener;
  6. /*是否能取消加载框*/
  7. private boolean cancel;
  8. /*是否显示加载框*/
  9. private boolean showProgress;
  10. /*是否需要缓存处理*/
  11. private boolean cache;
  12. /*基础url*/
  13. private String baseUrl= "http://www.izaodao.com/Api/";
  14. /*方法-如果需要缓存必须设置这个参数;不需要不用設置*/
  15. private String mothed;
  16. /*超时时间-默认6秒*/
  17. private int connectionTime = 6;
  18. /*有网情况下的本地缓存时间默认60秒*/
  19. private int cookieNetWorkTime= 60;
  20. /*无网络的情况下本地缓存时间默认30天*/
  21. private int cookieNoNetWorkTime= 24* 60* 60* 30;
  22. }

注释很详细,这里不具体描述了,由于这里是最后封装完成以后的代码,所以有些内容本章还会部分不会涉及,因为功能太多,还是按照一开始的博客章节讲解。

2.抽象api接口

  1. /**
  2. * 设置参数
  3. *
  4. * @param retrofit
  5. * @return
  6. */
  7. public abstract Observable getObservable(Retrofit retrofit);

通过子类也即是我们的具体api接口,通过getObservable实现service中定义的接口方法,例如:

  1. public class SubjectPostApi extends BaseApi {
  2. xxxxxxx
  3. xxxxxxx
  4. @Override
  5. public Observable getObservable(Retrofit retrofit) {
  6. HttpPostService service = retrofit.create(HttpPostService.class);
  7. return service.getAllVedioBys(isAll());
  8. }
  9. }

通过传入的Retrofit对象,可以随意切换挑选Service对象,得到定义的注解方法,初始完成以后返回Observable对象。

3.结果判断

这里结合RxJavamap方法在服务器返回数据中,统一处理数据处理,所以BaseApi<T> implements Func1<BaseResultEntity<T>, T>,后边结合结果处理链接起来使用

  1. @Override
  2. public T call(BaseResultEntity<T> httpResult) {
  3. if (httpResult.getRet() == 0) {
  4. throw new HttpTimeException(httpResult.getMsg());
  5. }
  6. return httpResult.getData();
  7. }

由于测试接口,也是当前我们公司接口都是有统一规则的,想必大家都有这样的接口规则,所以才有这里的统一判断,规则如下:

  1. * ret: 1成功, 2失败
  2. * msg:信息
  3. * data:{
  4. * name:视频名称
  5. * title:标题
  6. * }

其实上面的接口文档中就介绍了,统一先通过ret判断,失败显示msg信息,data是成功后的数据也就是用户关心的数据,所以可封装一个结果对象BaseResultEntity.

4.结果数据

  1. /**
  2. * 回调信息统一封装类
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public class BaseResultEntity<T> {
  6. // 判断标示
  7. private int ret;
  8. // 提示信息
  9. private String msg;
  10. //显示数据(用户需要关心的数据)
  11. private T data;
  12. xxxxx get-set xxxxx
  13. }

这里结合BaseApiFunc1判断,失败直接抛出一个异常,交个RxJavaonError处理,成功则将用户关心的数据传给Gson解析返回

5.泛型传递

BaseResultEntity<T>中的泛型T也就是我们所关心的回调数据,同样也是Gson最后解析返回的数据,传递的过程根节点是通过定义service方法是给定的,例如:

  1. public interface HttpPostService {
  2. @POST( "AppFiftyToneGraph/videoLink")
  3. Call<RetrofitEntity> getAllVedio(@Body boolean once_no);
  4. }

其中的RetrofitEntity就是用户关心的数据类,通过泛型传递给最后的接口。

6.强调

很多兄弟通过QQ群反馈给我说,使用一个接口需要写一个对应的api类继承BaseApi是不是很麻烦,我这里强调一下,这样封装是为了将一个Api接口作为一个对象去封装,个人觉得有必要封装成一个类,在日后工程日益增加接口随着增加的同时,对象的做法更加有利于查找接口和修改接口有利于迭代。


操作类封装

1初始对象

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

你真的会写单例吗

  1. private volatile static HttpManager INSTANCE;
  2. //构造方法私有
  3. private HttpManager() {
  4. }
  5. //获取单例
  6. public static HttpManager getInstance() {
  7. if (INSTANCE == null) {
  8. synchronized (HttpManager.class) {
  9. if (INSTANCE == null) {
  10. INSTANCE = new HttpManager();
  11. }
  12. }
  13. }
  14. return INSTANCE;
  15. }

2接口处理和回调处理:

  1. /**
  2. * 处理http请求
  3. *
  4. * @param basePar 封装的请求数据
  5. */
  6. public void doHttpDeal(BaseApi basePar) {
  7. //手动创建一个OkHttpClient并设置超时时间缓存等设置
  8. OkHttpClient.Builder builder = new OkHttpClient.Builder();
  9. builder.connectTimeout(basePar.getConnectionTime(), TimeUnit.SECONDS);
  10. builder.addInterceptor( new CookieInterceptor(basePar.isCache()));
  11. /*创建retrofit对象*/
  12. Retrofit retrofit = new Retrofit.Builder()
  13. .client(builder.build())
  14. .addConverterFactory(GsonConverterFactory.create())
  15. .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  16. .baseUrl(basePar.getBaseUrl())
  17. .build();
  18. /*rx处理*/
  19. ProgressSubscriber subscriber = new ProgressSubscriber(basePar);
  20. Observable observable = basePar.getObservable(retrofit)
  21. /*失败后的retry配置*/
  22. .retryWhen( new RetryWhenNetworkException())
  23. /*生命周期管理*/
  24. .compose(basePar.getRxAppCompatActivity().bindToLifecycle())
  25. /*http请求线程*/
  26. .subscribeOn(Schedulers.io())
  27. .unsubscribeOn(Schedulers.io())
  28. /*回调线程*/
  29. .observeOn(AndroidSchedulers.mainThread())
  30. /*结果判断*/
  31. .map(basePar);
  32. /*数据回调*/
  33. observable.subscribe(subscriber);
  34. }

首先通过api接口类BaseApi的实现类中数据初始化OkHttpClientRetrofit对象,其中包含了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代替

  1. /**
  2. * 用于在Http请求开始时,自动显示一个ProgressDialog
  3. * 在Http请求结束是,关闭ProgressDialog
  4. * 调用者自己对请求数据进行处理
  5. * Created by WZG on 2016/7/16.
  6. */
  7. public class ProgressSubscriber<T> extends Subscriber<T> {
  8. /*是否弹框*/
  9. private boolean showPorgress = true;
  10. /* 软引用回调接口*/
  11. private SoftReference<HttpOnNextListener> mSubscriberOnNextListener;
  12. /*软引用反正内存泄露*/
  13. private SoftReference<RxAppCompatActivity> mActivity;
  14. /*加载框可自己定义*/
  15. private ProgressDialog pd;
  16. /*请求数据*/
  17. private BaseApi api;
  18. /**
  19. * 构造
  20. *
  21. * @param api
  22. */
  23. public ProgressSubscriber(BaseApi api) {
  24. this.api = api;
  25. this.mSubscriberOnNextListener = api.getListener();
  26. this.mActivity = new SoftReference<>(api.getRxAppCompatActivity());
  27. setShowPorgress(api.isShowProgress());
  28. if (api.isShowProgress()) {
  29. initProgressDialog(api.isCancel());
  30. }
  31. }
  32. /**
  33. * 初始化加载框
  34. */
  35. private void initProgressDialog(boolean cancel) {
  36. Context context = mActivity.get();
  37. if (pd == null && context != null) {
  38. pd = new ProgressDialog(context);
  39. pd.setCancelable(cancel);
  40. if (cancel) {
  41. pd.setOnCancelListener( new DialogInterface.OnCancelListener() {
  42. @Override
  43. public void onCancel(DialogInterface dialogInterface) {
  44. onCancelProgress();
  45. }
  46. });
  47. }
  48. }
  49. }
  50. /**
  51. * 显示加载框
  52. */
  53. private void showProgressDialog() {
  54. if (!isShowPorgress()) return;
  55. Context context = mActivity.get();
  56. if (pd == null || context == null) return;
  57. if (!pd.isShowing()) {
  58. pd.show();
  59. }
  60. }
  61. /**
  62. * 隐藏
  63. */
  64. private void dismissProgressDialog() {
  65. if (!isShowPorgress()) return;
  66. if (pd != null && pd.isShowing()) {
  67. pd.dismiss();
  68. }
  69. }
  70. }

由于progress的特殊性,需要指定content而且不能是Application所以这里传递一个RxAppCompatActivity,而同时上面的HttpManager同样需要,所以这里统一还是按照BaseApi传递过来,使用软引用的方式避免泄露。剩下的无非是初始化,显示和关闭方法,可以详细看代码。

2.onStart()实现

onStart()中需要调用加载框,然后这里还有网络缓存的逻辑,后面会单独讲解,现在先忽略它的存在。

  1. /**
  2. * 订阅开始时调用
  3. * 显示ProgressDialog
  4. */
  5. @Override
  6. public void onStart() {
  7. showProgressDialog();
  8. /*缓存并且有网*/
  9. if (api.isCache() && AppUtil.isNetworkAvailable(RxRetrofitApp.getApplication())) {
  10. /*获取缓存数据*/
  11. CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(api.getUrl());
  12. if (cookieResulte != null) {
  13. long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
  14. if (time < api.getCookieNetWorkTime()) {
  15. if (mSubscriberOnNextListener.get() != null) {
  16. mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
  17. }
  18. onCompleted();
  19. unsubscribe();
  20. }
  21. }
  22. }
  23. }

3.onCompleted()实现

  1. /**
  2. * 完成,隐藏ProgressDialog
  3. */
  4. @Override
  5. public void onCompleted() {
  6. dismissProgressDialog();
  7. }

4.onError(Throwable e)实现

onError(Throwable e)是对错误信息的处理和缓存读取的处理,后续会讲解,先忽略。

  1. /**
  2. * 对错误进行统一处理
  3. * 隐藏ProgressDialog
  4. *
  5. * @param e
  6. */
  7. @Override
  8. public void onError(Throwable e) {
  9. dismissProgressDialog();
  10. /*需要緩存并且本地有缓存才返回*/
  11. if (api.isCache()) {
  12. Observable.just(api.getUrl()).subscribe( new Subscriber<String>() {
  13. @Override
  14. public void onCompleted() {
  15. }
  16. @Override
  17. public void onError(Throwable e) {
  18. errorDo(e);
  19. }
  20. @Override
  21. public void onNext(String s) {
  22. /*获取缓存数据*/
  23. CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(s);
  24. if (cookieResulte == null) {
  25. throw new HttpTimeException( "网络错误");
  26. }
  27. long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
  28. if (time < api.getCookieNoNetWorkTime()) {
  29. if (mSubscriberOnNextListener.get() != null) {
  30. mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
  31. }
  32. } else {
  33. CookieDbUtil.getInstance().deleteCookie(cookieResulte);
  34. throw new HttpTimeException( "网络错误");
  35. }
  36. }
  37. });
  38. } else {
  39. errorDo(e);
  40. }
  41. }
  42. /*错误统一处理*/
  43. private void errorDo(Throwable e) {
  44. Context context = mActivity.get();
  45. if (context == null) return;
  46. if (e instanceof SocketTimeoutException) {
  47. Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();
  48. } else if (e instanceof ConnectException) {
  49. Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();
  50. } else {
  51. Toast.makeText(context, "错误" + e.getMessage(), Toast.LENGTH_SHORT).show();
  52. }
  53. if (mSubscriberOnNextListener.get() != null) {
  54. mSubscriberOnNextListener.get().onError(e);
  55. }
  56. }

5.onNext(T t)实现

  1. /**
  2. * 将onNext方法中的返回结果交给Activity或Fragment自己处理
  3. *
  4. * @param t 创建Subscriber时的泛型类型
  5. */
  6. @Override
  7. public void onNext(T t) {
  8. if (mSubscriberOnNextListener.get() != null) {
  9. mSubscriberOnNextListener.get().onNext(t);
  10. }
  11. }

主要是是将得到的结果,通过自定义的接口返回给view界面,其中的软引用对象mSubscriberOnNextListener是自定义的接口回调类HttpOnNextListener.


6.HttpOnNextListener封装

现在只需关心onNext(T t)onError(Throwable e)接口即可,回调的触发点都是在上面的ProgressSubscriber中调用

  1. /**
  2. * 成功回调处理
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public abstract class HttpOnNextListener<T> {
  6. /**
  7. * 成功后回调方法
  8. * @param t
  9. */
  10. public abstract void onNext(T t);
  11. /**
  12. * 緩存回調結果
  13. * @param string
  14. */
  15. public void onCacheNext(String string){
  16. }
  17. /**
  18. * 失败或者错误方法
  19. * 主动调用,更加灵活
  20. * @param e
  21. */
  22. public void onError(Throwable e){
  23. }
  24. /**
  25. * 取消回調
  26. */
  27. public void onCancel(){
  28. }
  29. }

失败后的retry处理

这里你可能会问,Retrofit有自带的retry处理呀,的确Retrofit有自带的retry处理,但是有很多的局限,先看下使用

  1. OkHttpClient.Builder builder = new OkHttpClient.Builder();
  2. builder.retryOnConnectionFailure( true);

使用起来还是很方便,只需要调用一个方法即可,但是它是不可控的,也就是没有办法设置retry时间次数,所以不太灵活,既然如此还不如自己封装一下,因为用RxJava实现这个简直小菜,无形中好像已经给RxJava打了广告,中毒太深。

很简单直接上代码:

  1. /**
  2. * retry条件
  3. * Created by WZG on 2016/10/17.
  4. */
  5. public class RetryWhenNetworkException implements Func1<Observable<? extends Throwable>, Observable<?>> {
  6. // retry次数
  7. private int count = 3;
  8. // 延迟
  9. private long delay = 3000;
  10. // 叠加延迟
  11. private long increaseDelay = 3000;
  12. public RetryWhenNetworkException() {
  13. }
  14. public RetryWhenNetworkException(int count, long delay) {
  15. this.count = count;
  16. this.delay = delay;
  17. }
  18. public RetryWhenNetworkException(int count, long delay, long increaseDelay) {
  19. this.count = count;
  20. this.delay = delay;
  21. this.increaseDelay = increaseDelay;
  22. }
  23. @Override
  24. public Observable<?> call(Observable<? extends Throwable> observable) {
  25. return observable
  26. .zipWith(Observable.range( 1, count + 1), new Func2<Throwable, Integer, Wrapper>() {
  27. @Override
  28. public Wrapper call(Throwable throwable, Integer integer) {
  29. return new Wrapper(throwable, integer);
  30. }
  31. }).flatMap( new Func1<Wrapper, Observable<?>>() {
  32. @Override
  33. public Observable<?> call(Wrapper wrapper) {
  34. if ((wrapper.throwable instanceof ConnectException
  35. || wrapper.throwable instanceof SocketTimeoutException
  36. || wrapper.throwable instanceof TimeoutException)
  37. && wrapper.index < count + 1) { //如果超出重试次数也抛出错误,否则默认是会进入onCompleted
  38. return Observable.timer(delay + (wrapper.index - 1) * increaseDelay, TimeUnit.MILLISECONDS);
  39. }
  40. return Observable.error(wrapper.throwable);
  41. }
  42. });
  43. }
  44. private class Wrapper {
  45. private int index;
  46. private Throwable throwable;
  47. public Wrapper(Throwable throwable, int index) {
  48. this.index = index;
  49. this.throwable = throwable;
  50. }
  51. }
  52. }

使用

到这里,我们第一步封装已经完成了,下面讲解下如何使用,已经看明白的各位看官,估计早就看明白了使用方式,无非是创建一个api对象继承BaseApi初始接口信息,然后调用HttpManager对象的doHttpDeal(BaseApi basePar)方法,最后静静的等待回调类HttpOnNextListener<T>类返回的onNext(T t)成功数据或者onError(Throwable e)数据。

其实代码就是这样:

api接口对象

  1. /**
  2. * 测试数据
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public class SubjectPostApi extends BaseApi {
  6. // 接口需要传入的参数 可自定义不同类型
  7. private boolean all;
  8. /*任何你先要传递的参数*/
  9. // String xxxxx;
  10. /**
  11. * 默认初始化需要给定回调和rx周期类
  12. * 可以额外设置请求设置加载框显示,回调等(可扩展)
  13. * @param listener
  14. * @param rxAppCompatActivity
  15. */
  16. public SubjectPostApi(HttpOnNextListener listener, RxAppCompatActivity rxAppCompatActivity) {
  17. super(listener,rxAppCompatActivity);
  18. setShowProgress( true);
  19. setCancel( true);
  20. setCache( true);
  21. setMothed( "AppFiftyToneGraph/videoLink");
  22. setCookieNetWorkTime( 60);
  23. setCookieNoNetWorkTime( 24* 60* 60);
  24. }
  25. public boolean isAll() {
  26. return all;
  27. }
  28. public void setAll(boolean all) {
  29. this.all = all;
  30. }
  31. @Override
  32. public Observable getObservable(Retrofit retrofit) {
  33. HttpPostService service = retrofit.create(HttpPostService.class);
  34. return service.getAllVedioBys(isAll());
  35. }
  36. }

请求回调

  1. // 完美封装简化版
  2. private void simpleDo() {
  3. SubjectPostApi postEntity = new SubjectPostApi(simpleOnNextListener, this);
  4. postEntity.setAll( true);
  5. HttpManager manager = HttpManager.getInstance();
  6. manager.doHttpDeal(postEntity);
  7. }
  8. // 回调一一对应
  9. HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<SubjectResulte>>() {
  10. @Override
  11. public void onNext(List<SubjectResulte> subjects) {
  12. tvMsg.setText( "网络返回:\n" + subjects.toString());
  13. }
  14. @Override
  15. public void onCacheNext(String cache) {
  16. /*缓存回调*/
  17. Gson gson= new Gson();
  18. java.lang.reflect.Type type = new TypeToken<BaseResultEntity<List<SubjectResulte>>>() {}.getType();
  19. BaseResultEntity resultEntity= gson.fromJson(cache, type);
  20. tvMsg.setText( "缓存返回:\n"+resultEntity.getData().toString() );
  21. }
  22. /*用户主动调用,默认是不需要覆写该方法*/
  23. @Override
  24. public void onError(Throwable e) {
  25. super.onError(e);
  26. tvMsg.setText( "失败:\n" + e.toString());
  27. }
  28. /*用户主动调用,默认是不需要覆写该方法*/
  29. @Override
  30. public void onCancel() {
  31. super.onCancel();
  32. tvMsg.setText( "取消請求");
  33. }
  34. };

后续

到这里,封装功能中很多功能还没涉及和讲解,后续会陆续更新!
先给大家看看为师的完全体功能:

  1. 1.Retrofit+Rxjava+okhttp基本使用方法
  2. 2.统一处理请求数据格式
  3. 3.统一的ProgressDialog和回调Subscriber处理
  4. 4.取消http请求
  5. 5.预处理http请求
  6. 6.返回数据的统一判断
  7. 7.失败后的retry处理
  8. 8.RxLifecycle管理生命周期,防止泄露
  9. 9.文件上传下载(支持多文件,断点续传)
  10. 10.Cache数据持久化和数据库(greenDao)两种缓存机制
  11. 11.异常统一处理

来个图压压惊:

迫不及待的小伙伴可以看这里:

RxJava+Retrofit+OkHttp深入浅出-终极封装

但是其中有些后续优化迭代未及时更新,别生气,我马上补!


源码:

RxRetrofit-终极封装-深入浅出&网络请求-GitHub

其实我还有一个兄弟版本-传送门

我不会告诉你其实我还有个更加简单的版本


建议

如果你对这套封装有任何的问题和建议欢迎加入QQ群告诉我

版权声明:本文为博主原创文章,未经博主允许不得转载。如果本文对你有帮助,请关注下哦!如果有Android需求欢迎联系微信805858226 https://blog.csdn.net/u014610664/article/details/51939574
个人分类:  Rxjava/ReTrofit/okHttp极封
所属专栏:  Android RxJava之网络处理

RxJava+Retrofit+OkHttp深入浅出-终极封装二(网络请求)

背景:

CSDN博客发布了一系列的RxJava+Retrofit+OkHttp深入浅出-终极封装 之前发出后收到很多朋友的关注,原本只是自己学习后的一些经验总结,但是有同学运用到实战当中,这让我很惶恐,所有后续一直更新了很多次版本,有些地方难免有所变动导致之前的博客有所出入,正好最近受到掘金邀请内测博客,所以决定重新写一版,按照最后迭代完成的封装详细的讲述一遍,欢迎大家关注!

注意:由于本章的特殊性,后续文章比较长而且复杂,涉及内容也很多,所以大家准备好茶水,前方高能预警。


RxJava+Retrofit+OkHttp深入浅出-终极封装


封装成果

封装完以后,具有如下功能:

  1. 1.Retrofit+Rxjava+okhttp基本使用方法
  2. 2.统一处理请求数据格式
  3. 3.统一的ProgressDialog和回调Subscriber处理
  4. 4.取消http请求
  5. 5.预处理http请求
  6. 6.返回数据的统一判断
  7. 7.失败后的retry封装处理
  8. 8.RxLifecycle管理生命周期,防止泄露

实现效果:


具体使用

封装后http请求代码如下

  1. // 完美封装简化版
  2. private void simpleDo() {
  3. SubjectPost postEntity = new SubjectPost(simpleOnNextListener, this);
  4. postEntity.setAll( true);
  5. HttpManager manager = HttpManager.getInstance();
  6. manager.doHttpDeal(postEntity);
  7. }
  8. // 回调一一对应
  9. HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<Subject>>() {
  10. @Override
  11. public void onNext(List<Subject> subjects) {
  12. tvMsg.setText( "已封装:\n" + subjects.toString());
  13. }
  14. /*用户主动调用,默认是不需要覆写该方法*/
  15. @Override
  16. public void onError(Throwable e) {
  17. super.onError(e);
  18. tvMsg.setText( "失败:\n" + e.toString());
  19. }
  20. };


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


  1. /**
  2. * Retrofit加入rxjava实现http请求
  3. */
  4. private void onButton9Click() {
  5. //手动创建一个OkHttpClient并设置超时时间
  6. okhttp3.OkHttpClient.Builder builder = new OkHttpClient.Builder();
  7. builder.connectTimeout( 5, TimeUnit.SECONDS);
  8. Retrofit retrofit = new Retrofit.Builder()
  9. .client(builder.build())
  10. .addConverterFactory(GsonConverterFactory.create())
  11. .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  12. .baseUrl(HttpManager.BASE_URL)
  13. .build();
  14. / 加载框
  15. final ProgressDialog pd = new ProgressDialog( this);
  16. HttpService apiService = retrofit.create(HttpService.class);
  17. Observable<RetrofitEntity> observable = apiService.getAllVedioBy( true);
  18. observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
  19. .subscribe(
  20. new Subscriber<RetrofitEntity>() {
  21. @Override
  22. public void onCompleted() {
  23. if (pd != null && pd.isShowing()) {
  24. pd.dismiss();
  25. }
  26. }
  27. @Override
  28. public void onError(Throwable e) {
  29. if (pd != null && pd.isShowing()) {
  30. pd.dismiss();
  31. }
  32. }
  33. @Override
  34. public void onNext(RetrofitEntity retrofitEntity) {
  35. tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
  36. }
  37. @Override
  38. public void onStart() {
  39. super.onStart();
  40. pd.show();
  41. }
  42. }
  43. );
  44. }


可能你发现确是代码有点多,但是更加可怕的是,如果你一个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添加引用

  1. /*rx-android-java*/
  2. compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
  3. compile 'com.trello:rxlifecycle:1.0'
  4. compile 'com.trello:rxlifecycle-components:1.0'
  5. /*rotrofit*/
  6. compile 'com.squareup.retrofit2:retrofit:2.1.0'
  7. compile 'com.squareup.retrofit2:converter-gson:2.0.0'
  8. 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基本使用:

首先给定一个测试接口文档,后面的博客中我们都是用这个接口调试

  1. /**
  2. * @api videoLink 50音图视频链接
  3. * @url http://www.izaodao.com/Api/AppFiftyToneGraph/videoLink
  4. * @method post
  5. * @param once_no bool(选填,ture无链接) 一次性获取下载地址
  6. * @return json array(
  7. * ret:1成功,2失败
  8. * msg:信息
  9. * data:{
  10. * name:视频名称
  11. * title:标题
  12. * }
  13. )

1.初始化retrofit

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

  1. String BASE_URL = " http://www.izaodao.com/Api/"
  2. Retrofit retrofit = new Retrofit.Builder()
  3. .baseUrl(BASE_URL)
  4. .addConverterFactory(GsonConverterFactory.create())
  5. .build();


2.设置接口service

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

  1. /**
  2. * 接口地址
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public interface MyApiEndpointInterface {
  6. @POST( "AppFiftyToneGraph/videoLink")
  7. Call<RetrofitEntity> getAllVedio(@Body boolean once_no)
  8. }


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

  1. MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class);
  2. Call<RetrofitEntity> call = apiService.getAllVedio( true);
  3. call.enqueue( new Callback<RetrofitEntity>() {
  4. @Override
  5. public void onResponse(Response<RetrofitEntity> response, Retrofit retrofit) {
  6. RetrofitEntity entity = response.body();
  7. Log.i( "tag", "onResponse----->" + entity.getMsg());
  8. }
  9. @Override
  10. public void onFailure(Throwable t) {
  11. Log.i( "tag", "onFailure----->" + t.toString());
  12. }
  13. });

这就是简单的Retrofit使用步骤,接下来我们结合RxJava讲述


ReTrofit+Rxjava基本使用

对比之前的Retrofit使用

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

  1. @POST( "AppFiftyToneGraph/videoLink")
  2. Observable<RetrofitEntity> getAllVedioBy(@Body boolean once_no);

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

  1. Retrofit retrofit = new Retrofit.Builder()
  2. .client(builder.build())
  3. .addConverterFactory(GsonConverterFactory.create())
  4. .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  5. .baseUrl(HttpManager.BASE_URL)
  6. .build();

3.回调通过RxJava处理

  1. HttpService apiService = retrofit.create(HttpService.class);
  2. Observable<RetrofitEntity> observable = apiService.getAllVedioBy( true);
  3. observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
  4. .subscribe(
  5. new Subscriber<RetrofitEntity>() {
  6. @Override
  7. public void onCompleted() {
  8. }
  9. @Override
  10. public void onError(Throwable e) {
  11. }
  12. @Override
  13. public void onNext(RetrofitEntity retrofitEntity) {
  14. tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
  15. }
  16. }
  17. );

简单的RxJava集合Retrofit的使用就介绍完了,同样的可以发现使用起来很多重复性的代码,而且使用也不是那么简单,所以才有了下面的封装


ReTrofit+Rxjava进阶封装之路

先来一张流程图压压惊


请求数据封装

1.参数

首先需要封装的使我们的数据类,在数据类中需要封装请求中用到的相关数据的设置,比如请求参数、方法、加载框显示设置等等

  1. public abstract class BaseApi<T> implements Func1<BaseResultEntity<T>, T> {
  2. //rx生命周期管理
  3. private SoftReference<RxAppCompatActivity> rxAppCompatActivity;
  4. /*回调*/
  5. private SoftReference<HttpOnNextListener> listener;
  6. /*是否能取消加载框*/
  7. private boolean cancel;
  8. /*是否显示加载框*/
  9. private boolean showProgress;
  10. /*是否需要缓存处理*/
  11. private boolean cache;
  12. /*基础url*/
  13. private String baseUrl= "http://www.izaodao.com/Api/";
  14. /*方法-如果需要缓存必须设置这个参数;不需要不用設置*/
  15. private String mothed;
  16. /*超时时间-默认6秒*/
  17. private int connectionTime = 6;
  18. /*有网情况下的本地缓存时间默认60秒*/
  19. private int cookieNetWorkTime= 60;
  20. /*无网络的情况下本地缓存时间默认30天*/
  21. private int cookieNoNetWorkTime= 24* 60* 60* 30;
  22. }

注释很详细,这里不具体描述了,由于这里是最后封装完成以后的代码,所以有些内容本章还会部分不会涉及,因为功能太多,还是按照一开始的博客章节讲解。

2.抽象api接口

  1. /**
  2. * 设置参数
  3. *
  4. * @param retrofit
  5. * @return
  6. */
  7. public abstract Observable getObservable(Retrofit retrofit);

通过子类也即是我们的具体api接口,通过getObservable实现service中定义的接口方法,例如:

  1. public class SubjectPostApi extends BaseApi {
  2. xxxxxxx
  3. xxxxxxx
  4. @Override
  5. public Observable getObservable(Retrofit retrofit) {
  6. HttpPostService service = retrofit.create(HttpPostService.class);
  7. return service.getAllVedioBys(isAll());
  8. }
  9. }

通过传入的Retrofit对象,可以随意切换挑选Service对象,得到定义的注解方法,初始完成以后返回Observable对象。

3.结果判断

这里结合RxJavamap方法在服务器返回数据中,统一处理数据处理,所以BaseApi<T> implements Func1<BaseResultEntity<T>, T>,后边结合结果处理链接起来使用

  1. @Override
  2. public T call(BaseResultEntity<T> httpResult) {
  3. if (httpResult.getRet() == 0) {
  4. throw new HttpTimeException(httpResult.getMsg());
  5. }
  6. return httpResult.getData();
  7. }

由于测试接口,也是当前我们公司接口都是有统一规则的,想必大家都有这样的接口规则,所以才有这里的统一判断,规则如下:

  1. * ret: 1成功, 2失败
  2. * msg:信息
  3. * data:{
  4. * name:视频名称
  5. * title:标题
  6. * }

其实上面的接口文档中就介绍了,统一先通过ret判断,失败显示msg信息,data是成功后的数据也就是用户关心的数据,所以可封装一个结果对象BaseResultEntity.

4.结果数据

  1. /**
  2. * 回调信息统一封装类
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public class BaseResultEntity<T> {
  6. // 判断标示
  7. private int ret;
  8. // 提示信息
  9. private String msg;
  10. //显示数据(用户需要关心的数据)
  11. private T data;
  12. xxxxx get-set xxxxx
  13. }

这里结合BaseApiFunc1判断,失败直接抛出一个异常,交个RxJavaonError处理,成功则将用户关心的数据传给Gson解析返回

5.泛型传递

BaseResultEntity<T>中的泛型T也就是我们所关心的回调数据,同样也是Gson最后解析返回的数据,传递的过程根节点是通过定义service方法是给定的,例如:

  1. public interface HttpPostService {
  2. @POST( "AppFiftyToneGraph/videoLink")
  3. Call<RetrofitEntity> getAllVedio(@Body boolean once_no);
  4. }

其中的RetrofitEntity就是用户关心的数据类,通过泛型传递给最后的接口。

6.强调

很多兄弟通过QQ群反馈给我说,使用一个接口需要写一个对应的api类继承BaseApi是不是很麻烦,我这里强调一下,这样封装是为了将一个Api接口作为一个对象去封装,个人觉得有必要封装成一个类,在日后工程日益增加接口随着增加的同时,对象的做法更加有利于查找接口和修改接口有利于迭代。


操作类封装

1初始对象

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

你真的会写单例吗

  1. private volatile static HttpManager INSTANCE;
  2. //构造方法私有
  3. private HttpManager() {
  4. }
  5. //获取单例
  6. public static HttpManager getInstance() {
  7. if (INSTANCE == null) {
  8. synchronized (HttpManager.class) {
  9. if (INSTANCE == null) {
  10. INSTANCE = new HttpManager();
  11. }
  12. }
  13. }
  14. return INSTANCE;
  15. }

2接口处理和回调处理:

  1. /**
  2. * 处理http请求
  3. *
  4. * @param basePar 封装的请求数据
  5. */
  6. public void doHttpDeal(BaseApi basePar) {
  7. //手动创建一个OkHttpClient并设置超时时间缓存等设置
  8. OkHttpClient.Builder builder = new OkHttpClient.Builder();
  9. builder.connectTimeout(basePar.getConnectionTime(), TimeUnit.SECONDS);
  10. builder.addInterceptor( new CookieInterceptor(basePar.isCache()));
  11. /*创建retrofit对象*/
  12. Retrofit retrofit = new Retrofit.Builder()
  13. .client(builder.build())
  14. .addConverterFactory(GsonConverterFactory.create())
  15. .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  16. .baseUrl(basePar.getBaseUrl())
  17. .build();
  18. /*rx处理*/
  19. ProgressSubscriber subscriber = new ProgressSubscriber(basePar);
  20. Observable observable = basePar.getObservable(retrofit)
  21. /*失败后的retry配置*/
  22. .retryWhen( new RetryWhenNetworkException())
  23. /*生命周期管理*/
  24. .compose(basePar.getRxAppCompatActivity().bindToLifecycle())
  25. /*http请求线程*/
  26. .subscribeOn(Schedulers.io())
  27. .unsubscribeOn(Schedulers.io())
  28. /*回调线程*/
  29. .observeOn(AndroidSchedulers.mainThread())
  30. /*结果判断*/
  31. .map(basePar);
  32. /*数据回调*/
  33. observable.subscribe(subscriber);
  34. }

首先通过api接口类BaseApi的实现类中数据初始化OkHttpClientRetrofit对象,其中包含了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代替

  1. /**
  2. * 用于在Http请求开始时,自动显示一个ProgressDialog
  3. * 在Http请求结束是,关闭ProgressDialog
  4. * 调用者自己对请求数据进行处理
  5. * Created by WZG on 2016/7/16.
  6. */
  7. public class ProgressSubscriber<T> extends Subscriber<T> {
  8. /*是否弹框*/
  9. private boolean showPorgress = true;
  10. /* 软引用回调接口*/
  11. private SoftReference<HttpOnNextListener> mSubscriberOnNextListener;
  12. /*软引用反正内存泄露*/
  13. private SoftReference<RxAppCompatActivity> mActivity;
  14. /*加载框可自己定义*/
  15. private ProgressDialog pd;
  16. /*请求数据*/
  17. private BaseApi api;
  18. /**
  19. * 构造
  20. *
  21. * @param api
  22. */
  23. public ProgressSubscriber(BaseApi api) {
  24. this.api = api;
  25. this.mSubscriberOnNextListener = api.getListener();
  26. this.mActivity = new SoftReference<>(api.getRxAppCompatActivity());
  27. setShowPorgress(api.isShowProgress());
  28. if (api.isShowProgress()) {
  29. initProgressDialog(api.isCancel());
  30. }
  31. }
  32. /**
  33. * 初始化加载框
  34. */
  35. private void initProgressDialog(boolean cancel) {
  36. Context context = mActivity.get();
  37. if (pd == null && context != null) {
  38. pd = new ProgressDialog(context);
  39. pd.setCancelable(cancel);
  40. if (cancel) {
  41. pd.setOnCancelListener( new DialogInterface.OnCancelListener() {
  42. @Override
  43. public void onCancel(DialogInterface dialogInterface) {
  44. onCancelProgress();
  45. }
  46. });
  47. }
  48. }
  49. }
  50. /**
  51. * 显示加载框
  52. */
  53. private void showProgressDialog() {
  54. if (!isShowPorgress()) return;
  55. Context context = mActivity.get();
  56. if (pd == null || context == null) return;
  57. if (!pd.isShowing()) {
  58. pd.show();
  59. }
  60. }
  61. /**
  62. * 隐藏
  63. */
  64. private void dismissProgressDialog() {
  65. if (!isShowPorgress()) return;
  66. if (pd != null && pd.isShowing()) {
  67. pd.dismiss();
  68. }
  69. }
  70. }

由于progress的特殊性,需要指定content而且不能是Application所以这里传递一个RxAppCompatActivity,而同时上面的HttpManager同样需要,所以这里统一还是按照BaseApi传递过来,使用软引用的方式避免泄露。剩下的无非是初始化,显示和关闭方法,可以详细看代码。

2.onStart()实现

onStart()中需要调用加载框,然后这里还有网络缓存的逻辑,后面会单独讲解,现在先忽略它的存在。

  1. /**
  2. * 订阅开始时调用
  3. * 显示ProgressDialog
  4. */
  5. @Override
  6. public void onStart() {
  7. showProgressDialog();
  8. /*缓存并且有网*/
  9. if (api.isCache() && AppUtil.isNetworkAvailable(RxRetrofitApp.getApplication())) {
  10. /*获取缓存数据*/
  11. CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(api.getUrl());
  12. if (cookieResulte != null) {
  13. long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
  14. if (time < api.getCookieNetWorkTime()) {
  15. if (mSubscriberOnNextListener.get() != null) {
  16. mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
  17. }
  18. onCompleted();
  19. unsubscribe();
  20. }
  21. }
  22. }
  23. }

3.onCompleted()实现

  1. /**
  2. * 完成,隐藏ProgressDialog
  3. */
  4. @Override
  5. public void onCompleted() {
  6. dismissProgressDialog();
  7. }

4.onError(Throwable e)实现

onError(Throwable e)是对错误信息的处理和缓存读取的处理,后续会讲解,先忽略。

  1. /**
  2. * 对错误进行统一处理
  3. * 隐藏ProgressDialog
  4. *
  5. * @param e
  6. */
  7. @Override
  8. public void onError(Throwable e) {
  9. dismissProgressDialog();
  10. /*需要緩存并且本地有缓存才返回*/
  11. if (api.isCache()) {
  12. Observable.just(api.getUrl()).subscribe( new Subscriber<String>() {
  13. @Override
  14. public void onCompleted() {
  15. }
  16. @Override
  17. public void onError(Throwable e) {
  18. errorDo(e);
  19. }
  20. @Override
  21. public void onNext(String s) {
  22. /*获取缓存数据*/
  23. CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(s);
  24. if (cookieResulte == null) {
  25. throw new HttpTimeException( "网络错误");
  26. }
  27. long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
  28. if (time < api.getCookieNoNetWorkTime()) {
  29. if (mSubscriberOnNextListener.get() != null) {
  30. mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
  31. }
  32. } else {
  33. CookieDbUtil.getInstance().deleteCookie(cookieResulte);
  34. throw new HttpTimeException( "网络错误");
  35. }
  36. }
  37. });
  38. } else {
  39. errorDo(e);
  40. }
  41. }
  42. /*错误统一处理*/
  43. private void errorDo(Throwable e) {
  44. Context context = mActivity.get();
  45. if (context == null) return;
  46. if (e instanceof SocketTimeoutException) {
  47. Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();
  48. } else if (e instanceof ConnectException) {
  49. Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();
  50. } else {
  51. Toast.makeText(context, "错误" + e.getMessage(), Toast.LENGTH_SHORT).show();
  52. }
  53. if (mSubscriberOnNextListener.get() != null) {
  54. mSubscriberOnNextListener.get().onError(e);
  55. }
  56. }

5.onNext(T t)实现

  1. /**
  2. * 将onNext方法中的返回结果交给Activity或Fragment自己处理
  3. *
  4. * @param t 创建Subscriber时的泛型类型
  5. */
  6. @Override
  7. public void onNext(T t) {
  8. if (mSubscriberOnNextListener.get() != null) {
  9. mSubscriberOnNextListener.get().onNext(t);
  10. }
  11. }

主要是是将得到的结果,通过自定义的接口返回给view界面,其中的软引用对象mSubscriberOnNextListener是自定义的接口回调类HttpOnNextListener.


6.HttpOnNextListener封装

现在只需关心onNext(T t)onError(Throwable e)接口即可,回调的触发点都是在上面的ProgressSubscriber中调用

  1. /**
  2. * 成功回调处理
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public abstract class HttpOnNextListener<T> {
  6. /**
  7. * 成功后回调方法
  8. * @param t
  9. */
  10. public abstract void onNext(T t);
  11. /**
  12. * 緩存回調結果
  13. * @param string
  14. */
  15. public void onCacheNext(String string){
  16. }
  17. /**
  18. * 失败或者错误方法
  19. * 主动调用,更加灵活
  20. * @param e
  21. */
  22. public void onError(Throwable e){
  23. }
  24. /**
  25. * 取消回調
  26. */
  27. public void onCancel(){
  28. }
  29. }

失败后的retry处理

这里你可能会问,Retrofit有自带的retry处理呀,的确Retrofit有自带的retry处理,但是有很多的局限,先看下使用

  1. OkHttpClient.Builder builder = new OkHttpClient.Builder();
  2. builder.retryOnConnectionFailure( true);

使用起来还是很方便,只需要调用一个方法即可,但是它是不可控的,也就是没有办法设置retry时间次数,所以不太灵活,既然如此还不如自己封装一下,因为用RxJava实现这个简直小菜,无形中好像已经给RxJava打了广告,中毒太深。

很简单直接上代码:

  1. /**
  2. * retry条件
  3. * Created by WZG on 2016/10/17.
  4. */
  5. public class RetryWhenNetworkException implements Func1<Observable<? extends Throwable>, Observable<?>> {
  6. // retry次数
  7. private int count = 3;
  8. // 延迟
  9. private long delay = 3000;
  10. // 叠加延迟
  11. private long increaseDelay = 3000;
  12. public RetryWhenNetworkException() {
  13. }
  14. public RetryWhenNetworkException(int count, long delay) {
  15. this.count = count;
  16. this.delay = delay;
  17. }
  18. public RetryWhenNetworkException(int count, long delay, long increaseDelay) {
  19. this.count = count;
  20. this.delay = delay;
  21. this.increaseDelay = increaseDelay;
  22. }
  23. @Override
  24. public Observable<?> call(Observable<? extends Throwable> observable) {
  25. return observable
  26. .zipWith(Observable.range( 1, count + 1), new Func2<Throwable, Integer, Wrapper>() {
  27. @Override
  28. public Wrapper call(Throwable throwable, Integer integer) {
  29. return new Wrapper(throwable, integer);
  30. }
  31. }).flatMap( new Func1<Wrapper, Observable<?>>() {
  32. @Override
  33. public Observable<?> call(Wrapper wrapper) {
  34. if ((wrapper.throwable instanceof ConnectException
  35. || wrapper.throwable instanceof SocketTimeoutException
  36. || wrapper.throwable instanceof TimeoutException)
  37. && wrapper.index < count + 1) { //如果超出重试次数也抛出错误,否则默认是会进入onCompleted
  38. return Observable.timer(delay + (wrapper.index - 1) * increaseDelay, TimeUnit.MILLISECONDS);
  39. }
  40. return Observable.error(wrapper.throwable);
  41. }
  42. });
  43. }
  44. private class Wrapper {
  45. private int index;
  46. private Throwable throwable;
  47. public Wrapper(Throwable throwable, int index) {
  48. this.index = index;
  49. this.throwable = throwable;
  50. }
  51. }
  52. }

使用

到这里,我们第一步封装已经完成了,下面讲解下如何使用,已经看明白的各位看官,估计早就看明白了使用方式,无非是创建一个api对象继承BaseApi初始接口信息,然后调用HttpManager对象的doHttpDeal(BaseApi basePar)方法,最后静静的等待回调类HttpOnNextListener<T>类返回的onNext(T t)成功数据或者onError(Throwable e)数据。

其实代码就是这样:

api接口对象

  1. /**
  2. * 测试数据
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public class SubjectPostApi extends BaseApi {
  6. // 接口需要传入的参数 可自定义不同类型
  7. private boolean all;
  8. /*任何你先要传递的参数*/
  9. // String xxxxx;
  10. /**
  11. * 默认初始化需要给定回调和rx周期类
  12. * 可以额外设置请求设置加载框显示,回调等(可扩展)
  13. * @param listener
  14. * @param rxAppCompatActivity
  15. */
  16. public SubjectPostApi(HttpOnNextListener listener, RxAppCompatActivity rxAppCompatActivity) {
  17. super(listener,rxAppCompatActivity);
  18. setShowProgress( true);
  19. setCancel( true);
  20. setCache( true);
  21. setMothed( "AppFiftyToneGraph/videoLink");
  22. setCookieNetWorkTime( 60);
  23. setCookieNoNetWorkTime( 24* 60* 60);
  24. }
  25. public boolean isAll() {
  26. return all;
  27. }
  28. public void setAll(boolean all) {
  29. this.all = all;
  30. }
  31. @Override
  32. public Observable getObservable(Retrofit retrofit) {
  33. HttpPostService service = retrofit.create(HttpPostService.class);
  34. return service.getAllVedioBys(isAll());
  35. }
  36. }

请求回调

  1. // 完美封装简化版
  2. private void simpleDo() {
  3. SubjectPostApi postEntity = new SubjectPostApi(simpleOnNextListener, this);
  4. postEntity.setAll( true);
  5. HttpManager manager = HttpManager.getInstance();
  6. manager.doHttpDeal(postEntity);
  7. }
  8. // 回调一一对应
  9. HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<SubjectResulte>>() {
  10. @Override
  11. public void onNext(List<SubjectResulte> subjects) {
  12. tvMsg.setText( "网络返回:\n" + subjects.toString());
  13. }
  14. @Override
  15. public void onCacheNext(String cache) {
  16. /*缓存回调*/
  17. Gson gson= new Gson();
  18. java.lang.reflect.Type type = new TypeToken<BaseResultEntity<List<SubjectResulte>>>() {}.getType();
  19. BaseResultEntity resultEntity= gson.fromJson(cache, type);
  20. tvMsg.setText( "缓存返回:\n"+resultEntity.getData().toString() );
  21. }
  22. /*用户主动调用,默认是不需要覆写该方法*/
  23. @Override
  24. public void onError(Throwable e) {
  25. super.onError(e);
  26. tvMsg.setText( "失败:\n" + e.toString());
  27. }
  28. /*用户主动调用,默认是不需要覆写该方法*/
  29. @Override
  30. public void onCancel() {
  31. super.onCancel();
  32. tvMsg.setText( "取消請求");
  33. }
  34. };

后续

到这里,封装功能中很多功能还没涉及和讲解,后续会陆续更新!
先给大家看看为师的完全体功能:

  1. 1.Retrofit+Rxjava+okhttp基本使用方法
  2. 2.统一处理请求数据格式
  3. 3.统一的ProgressDialog和回调Subscriber处理
  4. 4.取消http请求
  5. 5.预处理http请求
  6. 6.返回数据的统一判断
  7. 7.失败后的retry处理
  8. 8.RxLifecycle管理生命周期,防止泄露
  9. 9.文件上传下载(支持多文件,断点续传)
  10. 10.Cache数据持久化和数据库(greenDao)两种缓存机制
  11. 11.异常统一处理

来个图压压惊:

迫不及待的小伙伴可以看这里:

RxJava+Retrofit+OkHttp深入浅出-终极封装

但是其中有些后续优化迭代未及时更新,别生气,我马上补!


源码:

RxRetrofit-终极封装-深入浅出&网络请求-GitHub

其实我还有一个兄弟版本-传送门

我不会告诉你其实我还有个更加简单的版本


建议

如果你对这套封装有任何的问题和建议欢迎加入QQ群告诉我

版权声明:本文为博主原创文章,未经博主允许不得转载。如果本文对你有帮助,请关注下哦!如果有Android需求欢迎联系微信805858226 https://blog.csdn.net/u014610664/article/details/51939574
个人分类:  Rxjava/ReTrofit/okHttp极封
所属专栏:  Android RxJava之网络处理
背景:

CSDN博客发布了一系列的RxJava+Retrofit+OkHttp深入浅出-终极封装 之前发出后收到很多朋友的关注,原本只是自己学习后的一些经验总结,但是有同学运用到实战当中,这让我很惶恐,所有后续一直更新了很多次版本,有些地方难免有所变动导致之前的博客有所出入,正好最近受到掘金邀请内测博客,所以决定重新写一版,按照最后迭代完成的封装详细的讲述一遍,欢迎大家关注!

注意:由于本章的特殊性,后续文章比较长而且复杂,涉及内容也很多,所以大家准备好茶水,前方高能预警。


RxJava+Retrofit+OkHttp深入浅出-终极封装


封装成果

封装完以后,具有如下功能:

  1. 1.Retrofit+Rxjava+okhttp基本使用方法
  2. 2.统一处理请求数据格式
  3. 3.统一的ProgressDialog和回调Subscriber处理
  4. 4.取消http请求
  5. 5.预处理http请求
  6. 6.返回数据的统一判断
  7. 7.失败后的retry封装处理
  8. 8.RxLifecycle管理生命周期,防止泄露

实现效果:


具体使用

封装后http请求代码如下

  1. // 完美封装简化版
  2. private void simpleDo() {
  3. SubjectPost postEntity = new SubjectPost(simpleOnNextListener, this);
  4. postEntity.setAll( true);
  5. HttpManager manager = HttpManager.getInstance();
  6. manager.doHttpDeal(postEntity);
  7. }
  8. // 回调一一对应
  9. HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<Subject>>() {
  10. @Override
  11. public void onNext(List<Subject> subjects) {
  12. tvMsg.setText( "已封装:\n" + subjects.toString());
  13. }
  14. /*用户主动调用,默认是不需要覆写该方法*/
  15. @Override
  16. public void onError(Throwable e) {
  17. super.onError(e);
  18. tvMsg.setText( "失败:\n" + e.toString());
  19. }
  20. };


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


  1. /**
  2. * Retrofit加入rxjava实现http请求
  3. */
  4. private void onButton9Click() {
  5. //手动创建一个OkHttpClient并设置超时时间
  6. okhttp3.OkHttpClient.Builder builder = new OkHttpClient.Builder();
  7. builder.connectTimeout( 5, TimeUnit.SECONDS);
  8. Retrofit retrofit = new Retrofit.Builder()
  9. .client(builder.build())
  10. .addConverterFactory(GsonConverterFactory.create())
  11. .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  12. .baseUrl(HttpManager.BASE_URL)
  13. .build();
  14. / 加载框
  15. final ProgressDialog pd = new ProgressDialog( this);
  16. HttpService apiService = retrofit.create(HttpService.class);
  17. Observable<RetrofitEntity> observable = apiService.getAllVedioBy( true);
  18. observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
  19. .subscribe(
  20. new Subscriber<RetrofitEntity>() {
  21. @Override
  22. public void onCompleted() {
  23. if (pd != null && pd.isShowing()) {
  24. pd.dismiss();
  25. }
  26. }
  27. @Override
  28. public void onError(Throwable e) {
  29. if (pd != null && pd.isShowing()) {
  30. pd.dismiss();
  31. }
  32. }
  33. @Override
  34. public void onNext(RetrofitEntity retrofitEntity) {
  35. tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
  36. }
  37. @Override
  38. public void onStart() {
  39. super.onStart();
  40. pd.show();
  41. }
  42. }
  43. );
  44. }


可能你发现确是代码有点多,但是更加可怕的是,如果你一个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添加引用

  1. /*rx-android-java*/
  2. compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
  3. compile 'com.trello:rxlifecycle:1.0'
  4. compile 'com.trello:rxlifecycle-components:1.0'
  5. /*rotrofit*/
  6. compile 'com.squareup.retrofit2:retrofit:2.1.0'
  7. compile 'com.squareup.retrofit2:converter-gson:2.0.0'
  8. 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基本使用:

首先给定一个测试接口文档,后面的博客中我们都是用这个接口调试

  1. /**
  2. * @api videoLink 50音图视频链接
  3. * @url http://www.izaodao.com/Api/AppFiftyToneGraph/videoLink
  4. * @method post
  5. * @param once_no bool(选填,ture无链接) 一次性获取下载地址
  6. * @return json array(
  7. * ret:1成功,2失败
  8. * msg:信息
  9. * data:{
  10. * name:视频名称
  11. * title:标题
  12. * }
  13. )

1.初始化retrofit

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

  1. String BASE_URL = " http://www.izaodao.com/Api/"
  2. Retrofit retrofit = new Retrofit.Builder()
  3. .baseUrl(BASE_URL)
  4. .addConverterFactory(GsonConverterFactory.create())
  5. .build();


2.设置接口service

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

  1. /**
  2. * 接口地址
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public interface MyApiEndpointInterface {
  6. @POST( "AppFiftyToneGraph/videoLink")
  7. Call<RetrofitEntity> getAllVedio(@Body boolean once_no)
  8. }


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

  1. MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class);
  2. Call<RetrofitEntity> call = apiService.getAllVedio( true);
  3. call.enqueue( new Callback<RetrofitEntity>() {
  4. @Override
  5. public void onResponse(Response<RetrofitEntity> response, Retrofit retrofit) {
  6. RetrofitEntity entity = response.body();
  7. Log.i( "tag", "onResponse----->" + entity.getMsg());
  8. }
  9. @Override
  10. public void onFailure(Throwable t) {
  11. Log.i( "tag", "onFailure----->" + t.toString());
  12. }
  13. });

这就是简单的Retrofit使用步骤,接下来我们结合RxJava讲述


ReTrofit+Rxjava基本使用

对比之前的Retrofit使用

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

  1. @POST( "AppFiftyToneGraph/videoLink")
  2. Observable<RetrofitEntity> getAllVedioBy(@Body boolean once_no);

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

  1. Retrofit retrofit = new Retrofit.Builder()
  2. .client(builder.build())
  3. .addConverterFactory(GsonConverterFactory.create())
  4. .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  5. .baseUrl(HttpManager.BASE_URL)
  6. .build();

3.回调通过RxJava处理

  1. HttpService apiService = retrofit.create(HttpService.class);
  2. Observable<RetrofitEntity> observable = apiService.getAllVedioBy( true);
  3. observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
  4. .subscribe(
  5. new Subscriber<RetrofitEntity>() {
  6. @Override
  7. public void onCompleted() {
  8. }
  9. @Override
  10. public void onError(Throwable e) {
  11. }
  12. @Override
  13. public void onNext(RetrofitEntity retrofitEntity) {
  14. tvMsg.setText( "无封装:\n" + retrofitEntity.getData().toString());
  15. }
  16. }
  17. );

简单的RxJava集合Retrofit的使用就介绍完了,同样的可以发现使用起来很多重复性的代码,而且使用也不是那么简单,所以才有了下面的封装


ReTrofit+Rxjava进阶封装之路

先来一张流程图压压惊


请求数据封装

1.参数

首先需要封装的使我们的数据类,在数据类中需要封装请求中用到的相关数据的设置,比如请求参数、方法、加载框显示设置等等

  1. public abstract class BaseApi<T> implements Func1<BaseResultEntity<T>, T> {
  2. //rx生命周期管理
  3. private SoftReference<RxAppCompatActivity> rxAppCompatActivity;
  4. /*回调*/
  5. private SoftReference<HttpOnNextListener> listener;
  6. /*是否能取消加载框*/
  7. private boolean cancel;
  8. /*是否显示加载框*/
  9. private boolean showProgress;
  10. /*是否需要缓存处理*/
  11. private boolean cache;
  12. /*基础url*/
  13. private String baseUrl= "http://www.izaodao.com/Api/";
  14. /*方法-如果需要缓存必须设置这个参数;不需要不用設置*/
  15. private String mothed;
  16. /*超时时间-默认6秒*/
  17. private int connectionTime = 6;
  18. /*有网情况下的本地缓存时间默认60秒*/
  19. private int cookieNetWorkTime= 60;
  20. /*无网络的情况下本地缓存时间默认30天*/
  21. private int cookieNoNetWorkTime= 24* 60* 60* 30;
  22. }

注释很详细,这里不具体描述了,由于这里是最后封装完成以后的代码,所以有些内容本章还会部分不会涉及,因为功能太多,还是按照一开始的博客章节讲解。

2.抽象api接口

  1. /**
  2. * 设置参数
  3. *
  4. * @param retrofit
  5. * @return
  6. */
  7. public abstract Observable getObservable(Retrofit retrofit);

通过子类也即是我们的具体api接口,通过getObservable实现service中定义的接口方法,例如:

  1. public class SubjectPostApi extends BaseApi {
  2. xxxxxxx
  3. xxxxxxx
  4. @Override
  5. public Observable getObservable(Retrofit retrofit) {
  6. HttpPostService service = retrofit.create(HttpPostService.class);
  7. return service.getAllVedioBys(isAll());
  8. }
  9. }

通过传入的Retrofit对象,可以随意切换挑选Service对象,得到定义的注解方法,初始完成以后返回Observable对象。

3.结果判断

这里结合RxJavamap方法在服务器返回数据中,统一处理数据处理,所以BaseApi<T> implements Func1<BaseResultEntity<T>, T>,后边结合结果处理链接起来使用

  1. @Override
  2. public T call(BaseResultEntity<T> httpResult) {
  3. if (httpResult.getRet() == 0) {
  4. throw new HttpTimeException(httpResult.getMsg());
  5. }
  6. return httpResult.getData();
  7. }

由于测试接口,也是当前我们公司接口都是有统一规则的,想必大家都有这样的接口规则,所以才有这里的统一判断,规则如下:

  1. * ret: 1成功, 2失败
  2. * msg:信息
  3. * data:{
  4. * name:视频名称
  5. * title:标题
  6. * }

其实上面的接口文档中就介绍了,统一先通过ret判断,失败显示msg信息,data是成功后的数据也就是用户关心的数据,所以可封装一个结果对象BaseResultEntity.

4.结果数据

  1. /**
  2. * 回调信息统一封装类
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public class BaseResultEntity<T> {
  6. // 判断标示
  7. private int ret;
  8. // 提示信息
  9. private String msg;
  10. //显示数据(用户需要关心的数据)
  11. private T data;
  12. xxxxx get-set xxxxx
  13. }

这里结合BaseApiFunc1判断,失败直接抛出一个异常,交个RxJavaonError处理,成功则将用户关心的数据传给Gson解析返回

5.泛型传递

BaseResultEntity<T>中的泛型T也就是我们所关心的回调数据,同样也是Gson最后解析返回的数据,传递的过程根节点是通过定义service方法是给定的,例如:

  1. public interface HttpPostService {
  2. @POST( "AppFiftyToneGraph/videoLink")
  3. Call<RetrofitEntity> getAllVedio(@Body boolean once_no);
  4. }

其中的RetrofitEntity就是用户关心的数据类,通过泛型传递给最后的接口。

6.强调

很多兄弟通过QQ群反馈给我说,使用一个接口需要写一个对应的api类继承BaseApi是不是很麻烦,我这里强调一下,这样封装是为了将一个Api接口作为一个对象去封装,个人觉得有必要封装成一个类,在日后工程日益增加接口随着增加的同时,对象的做法更加有利于查找接口和修改接口有利于迭代。


操作类封装

1初始对象

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

你真的会写单例吗

  1. private volatile static HttpManager INSTANCE;
  2. //构造方法私有
  3. private HttpManager() {
  4. }
  5. //获取单例
  6. public static HttpManager getInstance() {
  7. if (INSTANCE == null) {
  8. synchronized (HttpManager.class) {
  9. if (INSTANCE == null) {
  10. INSTANCE = new HttpManager();
  11. }
  12. }
  13. }
  14. return INSTANCE;
  15. }

2接口处理和回调处理:

  1. /**
  2. * 处理http请求
  3. *
  4. * @param basePar 封装的请求数据
  5. */
  6. public void doHttpDeal(BaseApi basePar) {
  7. //手动创建一个OkHttpClient并设置超时时间缓存等设置
  8. OkHttpClient.Builder builder = new OkHttpClient.Builder();
  9. builder.connectTimeout(basePar.getConnectionTime(), TimeUnit.SECONDS);
  10. builder.addInterceptor( new CookieInterceptor(basePar.isCache()));
  11. /*创建retrofit对象*/
  12. Retrofit retrofit = new Retrofit.Builder()
  13. .client(builder.build())
  14. .addConverterFactory(GsonConverterFactory.create())
  15. .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  16. .baseUrl(basePar.getBaseUrl())
  17. .build();
  18. /*rx处理*/
  19. ProgressSubscriber subscriber = new ProgressSubscriber(basePar);
  20. Observable observable = basePar.getObservable(retrofit)
  21. /*失败后的retry配置*/
  22. .retryWhen( new RetryWhenNetworkException())
  23. /*生命周期管理*/
  24. .compose(basePar.getRxAppCompatActivity().bindToLifecycle())
  25. /*http请求线程*/
  26. .subscribeOn(Schedulers.io())
  27. .unsubscribeOn(Schedulers.io())
  28. /*回调线程*/
  29. .observeOn(AndroidSchedulers.mainThread())
  30. /*结果判断*/
  31. .map(basePar);
  32. /*数据回调*/
  33. observable.subscribe(subscriber);
  34. }

首先通过api接口类BaseApi的实现类中数据初始化OkHttpClientRetrofit对象,其中包含了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代替

  1. /**
  2. * 用于在Http请求开始时,自动显示一个ProgressDialog
  3. * 在Http请求结束是,关闭ProgressDialog
  4. * 调用者自己对请求数据进行处理
  5. * Created by WZG on 2016/7/16.
  6. */
  7. public class ProgressSubscriber<T> extends Subscriber<T> {
  8. /*是否弹框*/
  9. private boolean showPorgress = true;
  10. /* 软引用回调接口*/
  11. private SoftReference<HttpOnNextListener> mSubscriberOnNextListener;
  12. /*软引用反正内存泄露*/
  13. private SoftReference<RxAppCompatActivity> mActivity;
  14. /*加载框可自己定义*/
  15. private ProgressDialog pd;
  16. /*请求数据*/
  17. private BaseApi api;
  18. /**
  19. * 构造
  20. *
  21. * @param api
  22. */
  23. public ProgressSubscriber(BaseApi api) {
  24. this.api = api;
  25. this.mSubscriberOnNextListener = api.getListener();
  26. this.mActivity = new SoftReference<>(api.getRxAppCompatActivity());
  27. setShowPorgress(api.isShowProgress());
  28. if (api.isShowProgress()) {
  29. initProgressDialog(api.isCancel());
  30. }
  31. }
  32. /**
  33. * 初始化加载框
  34. */
  35. private void initProgressDialog(boolean cancel) {
  36. Context context = mActivity.get();
  37. if (pd == null && context != null) {
  38. pd = new ProgressDialog(context);
  39. pd.setCancelable(cancel);
  40. if (cancel) {
  41. pd.setOnCancelListener( new DialogInterface.OnCancelListener() {
  42. @Override
  43. public void onCancel(DialogInterface dialogInterface) {
  44. onCancelProgress();
  45. }
  46. });
  47. }
  48. }
  49. }
  50. /**
  51. * 显示加载框
  52. */
  53. private void showProgressDialog() {
  54. if (!isShowPorgress()) return;
  55. Context context = mActivity.get();
  56. if (pd == null || context == null) return;
  57. if (!pd.isShowing()) {
  58. pd.show();
  59. }
  60. }
  61. /**
  62. * 隐藏
  63. */
  64. private void dismissProgressDialog() {
  65. if (!isShowPorgress()) return;
  66. if (pd != null && pd.isShowing()) {
  67. pd.dismiss();
  68. }
  69. }
  70. }

由于progress的特殊性,需要指定content而且不能是Application所以这里传递一个RxAppCompatActivity,而同时上面的HttpManager同样需要,所以这里统一还是按照BaseApi传递过来,使用软引用的方式避免泄露。剩下的无非是初始化,显示和关闭方法,可以详细看代码。

2.onStart()实现

onStart()中需要调用加载框,然后这里还有网络缓存的逻辑,后面会单独讲解,现在先忽略它的存在。

  1. /**
  2. * 订阅开始时调用
  3. * 显示ProgressDialog
  4. */
  5. @Override
  6. public void onStart() {
  7. showProgressDialog();
  8. /*缓存并且有网*/
  9. if (api.isCache() && AppUtil.isNetworkAvailable(RxRetrofitApp.getApplication())) {
  10. /*获取缓存数据*/
  11. CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(api.getUrl());
  12. if (cookieResulte != null) {
  13. long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
  14. if (time < api.getCookieNetWorkTime()) {
  15. if (mSubscriberOnNextListener.get() != null) {
  16. mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
  17. }
  18. onCompleted();
  19. unsubscribe();
  20. }
  21. }
  22. }
  23. }

3.onCompleted()实现

  1. /**
  2. * 完成,隐藏ProgressDialog
  3. */
  4. @Override
  5. public void onCompleted() {
  6. dismissProgressDialog();
  7. }

4.onError(Throwable e)实现

onError(Throwable e)是对错误信息的处理和缓存读取的处理,后续会讲解,先忽略。

  1. /**
  2. * 对错误进行统一处理
  3. * 隐藏ProgressDialog
  4. *
  5. * @param e
  6. */
  7. @Override
  8. public void onError(Throwable e) {
  9. dismissProgressDialog();
  10. /*需要緩存并且本地有缓存才返回*/
  11. if (api.isCache()) {
  12. Observable.just(api.getUrl()).subscribe( new Subscriber<String>() {
  13. @Override
  14. public void onCompleted() {
  15. }
  16. @Override
  17. public void onError(Throwable e) {
  18. errorDo(e);
  19. }
  20. @Override
  21. public void onNext(String s) {
  22. /*获取缓存数据*/
  23. CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(s);
  24. if (cookieResulte == null) {
  25. throw new HttpTimeException( "网络错误");
  26. }
  27. long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
  28. if (time < api.getCookieNoNetWorkTime()) {
  29. if (mSubscriberOnNextListener.get() != null) {
  30. mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
  31. }
  32. } else {
  33. CookieDbUtil.getInstance().deleteCookie(cookieResulte);
  34. throw new HttpTimeException( "网络错误");
  35. }
  36. }
  37. });
  38. } else {
  39. errorDo(e);
  40. }
  41. }
  42. /*错误统一处理*/
  43. private void errorDo(Throwable e) {
  44. Context context = mActivity.get();
  45. if (context == null) return;
  46. if (e instanceof SocketTimeoutException) {
  47. Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();
  48. } else if (e instanceof ConnectException) {
  49. Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();
  50. } else {
  51. Toast.makeText(context, "错误" + e.getMessage(), Toast.LENGTH_SHORT).show();
  52. }
  53. if (mSubscriberOnNextListener.get() != null) {
  54. mSubscriberOnNextListener.get().onError(e);
  55. }
  56. }

5.onNext(T t)实现

  1. /**
  2. * 将onNext方法中的返回结果交给Activity或Fragment自己处理
  3. *
  4. * @param t 创建Subscriber时的泛型类型
  5. */
  6. @Override
  7. public void onNext(T t) {
  8. if (mSubscriberOnNextListener.get() != null) {
  9. mSubscriberOnNextListener.get().onNext(t);
  10. }
  11. }

主要是是将得到的结果,通过自定义的接口返回给view界面,其中的软引用对象mSubscriberOnNextListener是自定义的接口回调类HttpOnNextListener.


6.HttpOnNextListener封装

现在只需关心onNext(T t)onError(Throwable e)接口即可,回调的触发点都是在上面的ProgressSubscriber中调用

  1. /**
  2. * 成功回调处理
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public abstract class HttpOnNextListener<T> {
  6. /**
  7. * 成功后回调方法
  8. * @param t
  9. */
  10. public abstract void onNext(T t);
  11. /**
  12. * 緩存回調結果
  13. * @param string
  14. */
  15. public void onCacheNext(String string){
  16. }
  17. /**
  18. * 失败或者错误方法
  19. * 主动调用,更加灵活
  20. * @param e
  21. */
  22. public void onError(Throwable e){
  23. }
  24. /**
  25. * 取消回調
  26. */
  27. public void onCancel(){
  28. }
  29. }

失败后的retry处理

这里你可能会问,Retrofit有自带的retry处理呀,的确Retrofit有自带的retry处理,但是有很多的局限,先看下使用

  1. OkHttpClient.Builder builder = new OkHttpClient.Builder();
  2. builder.retryOnConnectionFailure( true);

使用起来还是很方便,只需要调用一个方法即可,但是它是不可控的,也就是没有办法设置retry时间次数,所以不太灵活,既然如此还不如自己封装一下,因为用RxJava实现这个简直小菜,无形中好像已经给RxJava打了广告,中毒太深。

很简单直接上代码:

  1. /**
  2. * retry条件
  3. * Created by WZG on 2016/10/17.
  4. */
  5. public class RetryWhenNetworkException implements Func1<Observable<? extends Throwable>, Observable<?>> {
  6. // retry次数
  7. private int count = 3;
  8. // 延迟
  9. private long delay = 3000;
  10. // 叠加延迟
  11. private long increaseDelay = 3000;
  12. public RetryWhenNetworkException() {
  13. }
  14. public RetryWhenNetworkException(int count, long delay) {
  15. this.count = count;
  16. this.delay = delay;
  17. }
  18. public RetryWhenNetworkException(int count, long delay, long increaseDelay) {
  19. this.count = count;
  20. this.delay = delay;
  21. this.increaseDelay = increaseDelay;
  22. }
  23. @Override
  24. public Observable<?> call(Observable<? extends Throwable> observable) {
  25. return observable
  26. .zipWith(Observable.range( 1, count + 1), new Func2<Throwable, Integer, Wrapper>() {
  27. @Override
  28. public Wrapper call(Throwable throwable, Integer integer) {
  29. return new Wrapper(throwable, integer);
  30. }
  31. }).flatMap( new Func1<Wrapper, Observable<?>>() {
  32. @Override
  33. public Observable<?> call(Wrapper wrapper) {
  34. if ((wrapper.throwable instanceof ConnectException
  35. || wrapper.throwable instanceof SocketTimeoutException
  36. || wrapper.throwable instanceof TimeoutException)
  37. && wrapper.index < count + 1) { //如果超出重试次数也抛出错误,否则默认是会进入onCompleted
  38. return Observable.timer(delay + (wrapper.index - 1) * increaseDelay, TimeUnit.MILLISECONDS);
  39. }
  40. return Observable.error(wrapper.throwable);
  41. }
  42. });
  43. }
  44. private class Wrapper {
  45. private int index;
  46. private Throwable throwable;
  47. public Wrapper(Throwable throwable, int index) {
  48. this.index = index;
  49. this.throwable = throwable;
  50. }
  51. }
  52. }

使用

到这里,我们第一步封装已经完成了,下面讲解下如何使用,已经看明白的各位看官,估计早就看明白了使用方式,无非是创建一个api对象继承BaseApi初始接口信息,然后调用HttpManager对象的doHttpDeal(BaseApi basePar)方法,最后静静的等待回调类HttpOnNextListener<T>类返回的onNext(T t)成功数据或者onError(Throwable e)数据。

其实代码就是这样:

api接口对象

  1. /**
  2. * 测试数据
  3. * Created by WZG on 2016/7/16.
  4. */
  5. public class SubjectPostApi extends BaseApi {
  6. // 接口需要传入的参数 可自定义不同类型
  7. private boolean all;
  8. /*任何你先要传递的参数*/
  9. // String xxxxx;
  10. /**
  11. * 默认初始化需要给定回调和rx周期类
  12. * 可以额外设置请求设置加载框显示,回调等(可扩展)
  13. * @param listener
  14. * @param rxAppCompatActivity
  15. */
  16. public SubjectPostApi(HttpOnNextListener listener, RxAppCompatActivity rxAppCompatActivity) {
  17. super(listener,rxAppCompatActivity);
  18. setShowProgress( true);
  19. setCancel( true);
  20. setCache( true);
  21. setMothed( "AppFiftyToneGraph/videoLink");
  22. setCookieNetWorkTime( 60);
  23. setCookieNoNetWorkTime( 24* 60* 60);
  24. }
  25. public boolean isAll() {
  26. return all;
  27. }
  28. public void setAll(boolean all) {
  29. this.all = all;
  30. }
  31. @Override
  32. public Observable getObservable(Retrofit retrofit) {
  33. HttpPostService service = retrofit.create(HttpPostService.class);
  34. return service.getAllVedioBys(isAll());
  35. }
  36. }

请求回调

  1. // 完美封装简化版
  2. private void simpleDo() {
  3. SubjectPostApi postEntity = new SubjectPostApi(simpleOnNextListener, this);
  4. postEntity.setAll( true);
  5. HttpManager manager = HttpManager.getInstance();
  6. manager.doHttpDeal(postEntity);
  7. }
  8. // 回调一一对应
  9. HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<SubjectResulte>>() {
  10. @Override
  11. public void onNext(List<SubjectResulte> subjects) {
  12. tvMsg.setText( "网络返回:\n" + subjects.toString());
  13. }
  14. @Override
  15. public void onCacheNext(String cache) {
  16. /*缓存回调*/
  17. Gson gson= new Gson();
  18. java.lang.reflect.Type type = new TypeToken<BaseResultEntity<List<SubjectResulte>>>() {}.getType();
  19. BaseResultEntity resultEntity= gson.fromJson(cache, type);
  20. tvMsg.setText( "缓存返回:\n"+resultEntity.getData().toString() );
  21. }
  22. /*用户主动调用,默认是不需要覆写该方法*/
  23. @Override
  24. public void onError(Throwable e) {
  25. super.onError(e);
  26. tvMsg.setText( "失败:\n" + e.toString());
  27. }
  28. /*用户主动调用,默认是不需要覆写该方法*/
  29. @Override
  30. public void onCancel() {
  31. super.onCancel();
  32. tvMsg.setText( "取消請求");
  33. }
  34. };

后续

到这里,封装功能中很多功能还没涉及和讲解,后续会陆续更新!
先给大家看看为师的完全体功能:

  1. 1.Retrofit+Rxjava+okhttp基本使用方法
  2. 2.统一处理请求数据格式
  3. 3.统一的ProgressDialog和回调Subscriber处理
  4. 4.取消http请求
  5. 5.预处理http请求
  6. 6.返回数据的统一判断
  7. 7.失败后的retry处理
  8. 8.RxLifecycle管理生命周期,防止泄露
  9. 9.文件上传下载(支持多文件,断点续传)
  10. 10.Cache数据持久化和数据库(greenDao)两种缓存机制
  11. 11.异常统一处理

来个图压压惊:

迫不及待的小伙伴可以看这里:

RxJava+Retrofit+OkHttp深入浅出-终极封装

但是其中有些后续优化迭代未及时更新,别生气,我马上补!


源码:

RxRetrofit-终极封装-深入浅出&网络请求-GitHub

其实我还有一个兄弟版本-传送门

我不会告诉你其实我还有个更加简单的版本


建议

如果你对这套封装有任何的问题和建议欢迎加入QQ群告诉我

猜你喜欢

转载自blog.csdn.net/u012602304/article/details/80953992