该内容已被发布者删除 该内容被自由微信恢复
文章于 2017年8月23日 被检测为删除。
查看原文
被用户删除
其他

Rxjava +Retrofit 你需要掌握的几个技巧,Retrofit缓存,统一对有无网络处理, 异常处理,返回结果问题

2016-08-20 Tamic 开发者技术前线


Rxjava +Rterofit 需要掌握的几个技巧


取消订阅


  一般我们在视图消亡后,无需RxJava再执行,可以直接取消订阅

     observable.unsubscribeOn(Schedulers.io());

   可用在activity的 `onDestroy(),` Fragment的 `onDestroyView()`中调用

   还有种场景是借助rxJava请求网络数据,需要网络返回后保存数据并更新UI,这种情况视图已经消亡了必定会导致rxJava出错,导致App闪退,这种我们可以判断前的activity/view是否为空,并是否已showing,如果

   两者都不存在,即可无须更新UI。只处理保存数据即可。


订阅问题

   需要UI绘制后再进行订阅的场景,防止阻塞UI,我们需要延迟订阅执行。

   **立即订阅;**

             

             observable

                   .subscribeOn(Schedulers.io())

                   .observeOn(AndroidSchedulers.mainThread())

                   .subscribe(action);

                   

 延迟订阅

 

    observable.delay(2, TimeUnit.SECONDS)

                   .subscribeOn(Schedulers.io())

                   .observeOn(AndroidSchedulers.mainThread())

                   .subscribe(action);


基础Subscriber


   很多时候我们需要借用RxJava开启多个observable去读取网络,这是我们对不同Subscriber处理起来比较麻烦,因此统一对Subscriber对网络返回进行处理和, 有无网络做判断,甚至可以根据需求显示加载进度等

 构建抽象的BaseSubscribe类,只处理`start()`和`onCompleted()` ,上层处理时只处理`onError()`和`onNext()`

 

  

   public abstract class BaseSubscriber<T> extends Subscriber<T> {

       private BaseActivity context;

       public BaseSubscriber(BaseActivity context) {

           this.context = context;

       }

       @Override

       public void onStart() {

           super.onStart();

           if (!NetworkUtil.isNetworkAvailable(context)) {

               Toast.makeText(context, "当前网络不可用,请检查网络情况", Toast.LENGTH_SHORT).show();

              // 一定好主动调用下面这一句

               onCompleted();

               return;

           }

           // 显示进度条

           showLoadingProgress();

       }

       @Override

       public void onCompleted() {

          //关闭等待进度条

          closeLoadingProgress();

       }

    }


这样我们上层调用时只关心成功和失败即可无需关心网络情况

   

    observable..subscribeOn(Schedulers.io())

                   .observeOn(AndroidSchedulers.mainThread())

                   .subscribe(new BaseSubscriber<ResponseBody>(MainActivity.this) {

                    @Override

                    public void onError(Throwable e) {

                       Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();

                    }

                    @Override

                    public void onNext(ResponseBody responseBody) {

                        Toast.makeText(MainActivity.this, responseBody.toString(), Toast.LENGTH_LONG).show();

                    }

                });

      );


如果想对Error错误统一处理,也可以在BaseSubscriber处理onError(), 然后在callback上层,具体看自己项目情况,可以接着往下看

如果对成功结果进行处理,则可以将ResonseBody加入泛型<Response<T>> , Response一般是包含Code,MSg, Data的,在这里你可以中级判断code来进行业务分发,代码很简单,具体看文章结尾源码即可

如果你觉得目前的返回判断麻烦,也可以定义Response基类

     

    public class BaseResponse<T> {

      private int code;

      private String msg;

      private T data;

   }


这样我们在onNext() 只需统一判断状态码即可

                           @Override

                            public void onNext(BaseResponse<IpResult> responseBody) {

                                if (responseBody.isOk()) {

                                    IpResult ip = responseBody.getData();

                                    Toast.makeText(MainActivity.this, ip.toString(), Toast.LENGTH_LONG).show();

                                }

                            }


错误结果问题  

通过RXJva的 Func1来进行对原始的`Throwable`  进行包装转换

我们将原来Throwable  强转成自定义的 ResponeThrowable;

    private static class HttpResponseFunc《T》 implements Func1《Throwable, Observable《T》》 {

        @Override public Observable<T> call(Throwable t) {

            return Observable.error(ExceptionHandle.handleException(t));

        }

    }

                      

 **ResponeThrowable**     

    public static class ResponeThrowable extends Exception {

        public int code;

        public String message;

        public ResponeThrowable(Throwable throwable, int code) {

            super(throwable);

            this.code = code;

        }

    }


我们已经处理好强转工作后 继续把 `Func1`加到`Observable` 中:

因此这样用observable提供的onErrorResumeNext 则可以将你自定义的`Func1` 关联到错误处理类中:

      ((Observable) observable).onErrorResumeNext(new HttpResponseFunc<T>());


很可能你感觉有点不理解,这前提你需要了解RxJava的转义符和操作符`Observable.Transformer` 

还有`Func1`

这样我们对服务器返回的错误状态进行了自我的处理,再稍加翻译下便可以达到用户看懂的语言

这个类我参考**一叶扁舟**同学的案列,我再次做了改进:


 **ExceptionHandle 错误处理驱动**

  

    public class ExceptionHandle {

     private static final int UNAUTHORIZED = 401;

     private static final int FORBIDDEN = 403;

     private static final int NOT_FOUND = 404;

     private static final int REQUEST_TIMEOUT = 408;

     private static final int INTERNAL_SERVER_ERROR = 500;

     private static final int BAD_GATEWAY = 502;

     private static final int SERVICE_UNAVAILABLE = 503;

     private static final int GATEWAY_TIMEOUT = 504;

     public static ResponeThrowable handleException(Throwable e) {

        ResponeThrowable ex;

        if (e instanceof HttpException) {

            HttpException httpException = (HttpException) e;

            ex = new ResponeThrowable(e, ERROR.HTTP_ERROR);

            switch (httpException.code()) {

                case UNAUTHORIZED:

                case FORBIDDEN:

                case NOT_FOUND:

                case REQUEST_TIMEOUT:

                case GATEWAY_TIMEOUT:

                case INTERNAL_SERVER_ERROR:

                case BAD_GATEWAY:

                case SERVICE_UNAVAILABLE:

                default:

                    ex.message = "网络错误";

                    break;

            }

            return ex;

        } else if (e instanceof ServerException) {

            ServerException resultException = (ServerException) e;

            ex = new ResponeThrowable(resultException, resultException.code);

            ex.message = resultException.message;

            return ex;

        } else if (e instanceof JsonParseException

                || e instanceof JSONException

                || e instanceof ParseException) {

            ex = new ResponeThrowable(e, ERROR.PARSE_ERROR);

            ex.message = "解析错误";

            return ex;

        } else if (e instanceof ConnectException) {

            ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);

            ex.message = "连接失败";

            return ex;

        } else if (e instanceof javax.net.ssl.SSLHandshakeException) {

            ex = new ResponeThrowable(e, ERROR.SSL_ERROR);

            ex.message = "证书验证失败";

            return ex;

        }

        else {

            ex = new ResponeThrowable(e, ERROR.UNKNOWN);

            ex.message = "未知错误";

            return ex;

        }

    }

    /**

     * 约定异常

     */

    class ERROR {

        /**

         * 未知错误

         */

        public static final int UNKNOWN = 1000;

        /**

         * 解析错误

         */

        public static final int PARSE_ERROR = 1001;

        /**

         * 网络错误

         */

        public static final int NETWORD_ERROR = 1002;

        /**

         * 协议出错

         */

        public static final int HTTP_ERROR = 1003;

        /**

         * 证书出错

         */

        public static final int SSL_ERROR = 1005;

    }

    public static class ResponeThrowable extends Exception {

        public int code;

        public String message;

        public ResponeThrowable(Throwable throwable, int code) {

            super(throwable);

            this.code = code;

        }

     }

     public class ServerException extends RuntimeException {

        public int code;

        public String message;

     }

    }

 接着可以在 BaseSubscriber<T>中处理异常拉

    public abstract class BaseSubscriber<T> extends Subscriber<T> {

    private Context context;

   

    public BaseSubscriber(Context context) {

        this.context = context;

    }

    @Override

    public void onError(Throwable e) {

        Log.e("Tamic", e.getMessage());

        // todo error somthing

        if(e instanceof ExceptionHandle.ResponeThrowable){

            onError((ExceptionHandle.ResponeThrowable)e);

        } else {

            onError(new ExceptionHandle.ResponeThrowable(e, ExceptionHandle.ERROR.UNKNOWN));

        }

     }

    }


最后上层调用就是这样了:

     RetrofitClient.getInstance(MainActivity.this).createBaseApi().getData(new   BaseSubscriber<IpResult>(MainActivity.this) {

                    @Override

                    public void onError(ResponeThrowable e) {

                       // 处理翻译后异常。

                        Log.e("Tamic", e.code + " "+ e.message);

                        Toast.makeText(MainActivity.this, e.message, Toast.LENGTH_LONG).show();

                    }

                    @Override

                    public void onNext(IpResult responseBody) {

                        Toast.makeText(MainActivity.this, responseBody.toString(), Toast.LENGTH_LONG).show();

                    }

                }, "21.22.11.33");


指的注意的是上层使用BaseSubscriber 的实现类和子类即可,如果你要重写BaseSubscriber 的onStat()和onCompleted() 也是可以的

#缓存问题

有时候需要在无网络时增加缓存功能,因此给Retrofit加入基础拦截器,来处理缓存问题

/**

    * BaseInterceptor

    * Created by Tamic on 2016-7-15.

    */

public class BaseInterceptor implements Interceptor{

    private Map<String, String> headers;

    private Context context;

    public BaseInterceptor(Map<String, String> headers, Context context) {

        this.headers = headers;

        this.context = context;

    }

    @Override

    public Response intercept(Chain chain) throws IOException {

        Request.Builder builder = chain.request()

                .newBuilder();

        builder.cacheControl(CacheControl.FORCE_CACHE).url(chain.request().url())

        .build();

        if (!NetworkUtil.isNetworkAvailable(context)) {

            ((Activity)context).runOnUiThread(new Runnable() {

                @Override

                public void run() {

                    Toast.makeText(context, "当前无网络!", Toast.LENGTH_SHORT).show();

                }

            });

        }

        if (headers != null && headers.size() > 0) {

            Set<String> keys = headers.keySet();

            for (String headerKey : keys) {

                builder.addHeader(headerKey, headers.get(headerKey)).build();

            }

        }

        if (NetworkUtil.isNetworkAvailable(context)) {

            int maxAge = 60; // read from cache for 60 s

            builder

                    .removeHeader("Pragma")

                    .addHeader("Cache-Control", "public, max-age=" + maxAge)

                    .build();

        } else {

            int maxStale = 60 * 60 * 24 * 14; // tolerate 2-weeks stale

            builder

                    .removeHeader("Pragma")

                    .addHeader("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)

                    .build();

        }

        return chain.proceed(builder.build());

    }

}

```

okHttpClient加入拦截器

           okHttpClient = new OkHttpClient.Builder()

              

                .addInterceptor(new BaseInterceptor(headers, context))

                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)

                .build();

Retrofit 加入okhttpClient

```

retrofit = new Retrofit.Builder()

                .client(okHttpClient)

                .baseUrl(url)

                .build();

```

如果你不想用okhttp自带的缓存策略,因为这需要服务端配合处理缓存请求头,不然会抛出: **HTTP 504 Unsatisfiable Request (only-if-cached)**

除了以上修改 Request.cacheControl的方式实现缓存,也可以自定义一个Cahe策略用来实现本地硬缓存。

构建CaheManager,用Url对应Json实现,此类非常简单,你可以自己实现,时间策略可自我加入扩展

在BaseSubscriber进行网络判断,加载缓存数据返回妥妥的;

     @Override

     public void onStart() {

        super.onStart();

        Toast.makeText(context, "http is start", Toast.LENGTH_SHORT).show();

        // todo some common as show loadding  and check netWork is NetworkAvailable

        // if  NetworkAvailable no !   must to call onCompleted

        if (!NetworkUtil.isNetworkAvailable(context)) {

            Toast.makeText(context, "无网络", Toast.LENGTH_SHORT).show();

            if (isNeedCahe) {

                Toast.makeText(context, "无网络,已智能读取缓存!", Toast.LENGTH_SHORT).show();

                IpResult ipResult = new Gson().fromJson(CaheManager.getjson(url), IpResult.class);

                onNext((T) ipResult);

            }

            onCompleted();

        }

    }


总结

通过这次的整理,再进行RxJava和Retrofit中 ,所有坑直接添就行,具体可以点击原文查看




长按识别二维码关注



微信ID:大白那些事儿

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存