美文网首页NetAndroid-RxJavaLet's Android
Android RxJava+Retrofit完美封装(缓存,请

Android RxJava+Retrofit完美封装(缓存,请

作者: _小河马 | 来源:发表于2016-11-10 17:20 被阅读29072次
    *文章已授权微信公众号 guolin_blog (郭霖)独家发布
    拖拖踏踏的第三篇文章,我又来造轮子了,一直纠结要不要写这个主题的文章,总感觉的自己驾驭不了RxJava这么高深的东西。本篇可能比较多的是个人的理解。
    ------------- 2018-05-21更新--------------

    升级为Retrofit2.0+RxJava2 的版本,项目结构做了一些修改。项目地址https://github.com/Hemumu/Template

    前言

    RetrofitRxJava已经出来很久了,很多前辈写了很多不错的文章,在此不得不感谢这些前辈无私奉献的开源精神,能让我们站在巨人的肩膀上望得更远。对于 RxJava 不是很了解的同学推荐你们看扔物线大神的这篇文章给 Android 开发者的 RxJava 详解一遍看不懂就看第二遍。Retrofit的使用可以参考Android Retrofit 2.0使用

    本文内容是基于Retrofit + RxJava做的一些巧妙的封装。参考了很多文章加入了一些自己的理解,请多指教。源码地址https://github.com/Hemumu/RxSample

    先放出build.gradle

        compile 'io.reactivex:rxjava:1.1.0'
        compile 'io.reactivex:rxandroid:1.1.0'
        compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
        compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
        compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
    

    本文是基于RxJava1.1.0Retrofit 2.0.0-beta4来进行的。

    初始化 Retrofit

    新建类Api,此类就是初始化Retrofit,提供一个静态方法初始化Retrofit非常简单.

        private static ApiService SERVICE;
        /**
         * 请求超时时间
         */
        private static final int DEFAULT_TIMEOUT = 10000;
    
        public static ApiService getDefault() {
            if (SERVICE == null) {
                //手动创建一个OkHttpClient并设置超时时间
                OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
                httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                /**
                 * 对所有请求添加请求头
                 */
                httpClientBuilder.addInterceptor(new Interceptor() {
                    @Override
                    public okhttp3.Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        okhttp3.Response originalResponse = chain.proceed(request);
                        return originalResponse.newBuilder().header("key1", "value1").addHeader("key2", "value2").build();
                    }
                });
                SERVICE = new Retrofit.Builder()
                        .client(httpClientBuilder.build())
                        .addConverterFactory(GsonConverterFactory.create())
                        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                        .baseUrl(Url.BASE_URL)
                        .build().create(ApiService.class);
            }
            return SERVICE;
        }
    

    提供一个静态方法初始化Retrofit,手动创建了OkHttpClient设置了请求的超时时间。并在OkHttp的拦截器中增加了请求头。注意这里是为所有的请求添加了请求头,你可以单独的给请求增加请求头,例如

        @Headers("apikey:b86c2269fe6588bbe3b41924bb2f2da2")
        @GET("/student/login")
        Observable<HttpResult> login(@Query("phone") String phone,  @Query("password") String psw);
    

    Retrofit初始化不同的地方就在我们添加了这两句话

    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    

    service的定义也从这样

    @GET("/student/login")
    Call<HttpResult> getTopMovie(@Query("start") int start, @Query("count") int count);
    

    变成了

    @GET("/student/login")
    Observable<HttpResult> login(@Query("phone") String phone,  @Query("password") String psw);
    

    返回值变成了Observable,这个Observable不就是RxJava的可观察者(即被观察者)么。

    封装服务器请求以及返回数据

    用户在使用任何一个网络框架都只关系请求的返回和错误信息,所以对请求的返回和请求要做一个细致的封装。

    我们一般请求的返回都是像下面这样

    {
       "code":"200",
       "message":"Return Successd!",
       "data":{
             "name":"张三"
              "age":3
       }
    }
    

    如果你们的服务器返回不是这样的格式那你就只有坐下来请他喝茶,跟他好好说(把他头摁进显示器)了。大不了就献出你的菊花吧!

    对于这样的数据我们肯定要对code做出一些判断,不同的code对应不同的错误信息。所以我们新建一个HttpResult类,对应上面的数据结构。

    public class HttpResult<T> {
    
        private int code;
        private String message;
        private T data;
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
        
    }
    

    这算是所有实体的一个基类,data可以为任何数据类型。

    我们要对所以返回结果进行预处理,新建一个RxHelper,预处理无非就是对code进行判断和解析,不同的错误返回不同的错误信息,这还不简单。Rxjavamap操作符不是轻松解决

    Api.getDefault().login("name","psw")
         .map(new HttpResultFunc<UserEntity>());
         .subscribeOn(Schedulers.io())
         .unsubscribeOn(Schedulers.io())
         .subscribeOn(AndroidSchedulers.mainThread())
         .observeOn(AndroidSchedulers.mainThread())
         .subscribe(subscriber);
    
    
        private class HttpResultFunc<T> implements Func1<HttpResult<T>, T> {
            @Override
            public T call(HttpResult<T> httpResult) {
                Log.e("error", httpResult.getData().toString() + "");
                if (httpResult.getCode() != 0) {
                    throw new ApiException(httpResult.getCode());
                }
                return httpResult.getData();
            }
        }
    
    

    哟,这不是轻松愉快 so seay么!对code进行了判断,code为0就做对应更新UI或者其他后续操作,不等于0就抛出异常,在ApiException中队code做处理,根据message字段进行提示用户

        private static String getApiExceptionMessage(int code){
            switch (code) {
                case USER_NOT_EXIST:
                    message = "该用户不存在";
                    break;
                case WRONG_PASSWORD:
                    message = "密码错误";
                    break;
                default:
                    message = "未知错误";
            }
            return message;
        }
    

    撒花!!!

    然而。。。RxJava永远比你想象的强大。RxJava中那么多操作符看到我身体不适,有个操作符compose。因为我们在每一个请求中都会处理code以及一些重用一些操作符,比如用observeOnsubscribeOn来切换线程。RxJava提供了一种解决方案:Transformer(转换器),一般情况下就是通过使用操作符Observable.compose()来实现。具体可以参考避免打断链式结构:使用.compose( )操作符

    新建一个RxHelper对结果进行预处理,代码

    public class RxHelper {
        /**
         * 对结果进行预处理
         *
         * @param <T>
         * @return
         */
        public static <T> Observable.Transformer<HttpResult<T>, T> handleResult() {
            return new Observable.Transformer<HttpResult<T>, T>() {
                @Override
                public Observable<T> call(Observable<HttpResult<T>> tObservable) {
                    return tObservable.flatMap(new Func1<HttpResult<T>, Observable<T>>() {
                        @Override
                        public Observable<T> call(HttpResult<T> result) {
                            LogUtils.e(result.getCode()+"");
                            if (result.getCode() == 0) {
                                return createData(result.getData());
                            } else {
                                return Observable.error(new ApiException(result.getCode()));
                            }
                        }
                    }).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).subscribeOn(AndroidSchedulers.mainThread()).observeOn(AndroidSchedulers.mainThread());
                }
            };
        }
    
        /**
         * 创建成功的数据
         *
         * @param data
         * @param <T>
         * @return
         */
        private static <T> Observable<T> createData(final T data) {
            return Observable.create(new Observable.OnSubscribe<T>() {
                @Override
                public void call(Subscriber<? super T> subscriber) {
                    try {
                        subscriber.onNext(data);
                        subscriber.onCompleted();
                    } catch (Exception e) {
                        subscriber.onError(e);
                    }
                }
            });
        }
    }
    

    Transformer实际上就是一个Func1<Observable<T>, Observable<R>>,换言之就是:可以通过它将一种类型的Observable转换成另一种类型的Observable,和调用一系列的内联操作符是一模一样的。这里我们首先使用flatMap操作符把Obserable<HttpResult<T>>,转换成为Observable<T>在内部对code进行了预处理。如果成功则把结果Observable<T>发射给订阅者。反之则把code交给ApiException并返回一个异常,ApiException中我们对code进行相应的处理并返回对应的错误信息

    
    public class ApiException extends RuntimeException{
    
        public static final int USER_NOT_EXIST = 100;
        public static final int WRONG_PASSWORD = 101;
        private static String message;
    
        public ApiException(int resultCode) {
            this(getApiExceptionMessage(resultCode));
        }
    
        public ApiException(String detailMessage) {
            super(detailMessage);
        }
    
        @Override
        public String getMessage() {
            return message;
        }
    
        /**
         * 由于服务器传递过来的错误信息直接给用户看的话,用户未必能够理解
         * 需要根据错误码对错误信息进行一个转换,在显示给用户
         * @param code
         * @return
         */
        private static String getApiExceptionMessage(int code){
            switch (code) {
                case USER_NOT_EXIST:
                    message = "该用户不存在";
                    break;
                case WRONG_PASSWORD:
                    message = "密码错误";
                    break;
                default:
                    message = "未知错误";
            }
            return message;
        }
    }
    

    最后调用了频繁使用的subscribeOn()observeOn()以及unsubscribeOn()

    处理ProgressDialog

    Rxjava中我们什么时候来显示Dialog呢。一开始觉得是放在Subscriber<T>onStart中。onStart可以用作流程开始前的初始化。然而 onStart()由于在 subscribe()发生时就被调用了,因此不能指定线程,而是只能执行在 subscribe()被调用时的线程。所以onStart并不能保证永远在主线程运行。

    怎么办呢?

    千万不要小看了RxJava,与 onStart()相对应的有一个方法 doOnSubscribe(),它和 onStart()同样是在subscribe()调用后而且在事件发送前执行,但区别在于它可以指定线程。默认情况下, doOnSubscribe()执行在 subscribe()发生的线程;而如果在 doOnSubscribe()之后有 subscribeOn()的话,它将执行在离它最近的subscribeOn()所指定的线程。可以看到在RxHelper中看到我们调用了两次subscribeOn,最后一个调用也就是离doOnSubscribe()最近的一次subscribeOn是指定的AndroidSchedulers.mainThread()也就是主线程。这样我们就就能保证它永远都在主线运行了。这里不得不感概RxJava的强大。

    这里我们自定义一个类ProgressSubscriber继承Subscriber<T>

    public  abstract class ProgressSubscriber<T> extends Subscriber<T> implements ProgressCancelListener{
    
    
        private SimpleLoadDialog dialogHandler;
    
        public ProgressSubscriber(Context context) {
            dialogHandler = new SimpleLoadDialog(context,this,true);
        }
    
        @Override
        public void onCompleted() {
            dismissProgressDialog();
        }
    
    
        /**
         * 显示Dialog
         */
        public void showProgressDialog(){
            if (dialogHandler != null) {
                dialogHandler.obtainMessage(SimpleLoadDialog.SHOW_PROGRESS_DIALOG).sendToTarget();
            }
        }
    
        @Override
        public void onNext(T t) {
            _onNext(t);
        }
    
        /**
         * 隐藏Dialog
         */
        private void dismissProgressDialog(){
            if (dialogHandler != null) {
                dialogHandler.obtainMessage(SimpleLoadDialog.DISMISS_PROGRESS_DIALOG).sendToTarget();
                dialogHandler=null;
            }
        }
        @Override
        public void onError(Throwable e) {
            e.printStackTrace();
            if (false) { //这里自行替换判断网络的代码
                _onError("网络不可用");
            } else if (e instanceof ApiException) {
                _onError(e.getMessage());
            } else {
                _onError("请求失败,请稍后再试...");
            }
            dismissProgressDialog();
        }
    
    
        @Override
        public void onCancelProgress() {
            if (!this.isUnsubscribed()) {
                this.unsubscribe();
            }
        }
        protected abstract void _onNext(T t);
        protected abstract void _onError(String message);
    }
    
    

    初始化ProgressSubscriber新建了一个我们自己定义的ProgressDialog并且传入一个自定义接口ProgressCancelListener。此接口是在SimpleLoadDialog消失onCancel的时候回调的。用于终止网络请求。

      load.setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialog) {
                        mProgressCancelListener.onCancelProgress();
                    }
        });
    

    ProgressSubscriber其他就很简单了,在onCompleted()onError()的时候取消Dialog。需要的时候调用showProgressDialog即可。

    处理数据缓存

    服务器返回的数据我们肯定要做缓存,所以我们需要一个RetrofitCache类来做缓存处理。

    public class RetrofitCache {
        /**
         * @param cacheKey 缓存的Key
         * @param fromNetwork
         * @param isSave       是否缓存
         * @param forceRefresh 是否强制刷新
         * @param <T>
         * @return
         */
        public static <T> Observable<T> load(final String cacheKey,
                                             Observable<T> fromNetwork,
                                             boolean isSave, boolean forceRefresh) {
            Observable<T> fromCache = Observable.create(new Observable.OnSubscribe<T>() {
                @Override
                public void call(Subscriber<? super T> subscriber) {
                    T cache = (T) Hawk.get(cacheKey);
                    if (cache != null) {
                        subscriber.onNext(cache);
                    } else {
                        subscriber.onCompleted();
                    }
                }
            }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
            //是否缓存
            if (isSave) {
                /**
                 * 这里的fromNetwork 不需要指定Schedule,在handleRequest中已经变换了
                 */
                fromNetwork = fromNetwork.map(new Func1<T, T>() {
                    @Override
                    public T call(T result) {
                        Hawk.put(cacheKey, result);
                        return result;
                    }
                });
            }
            //强制刷新
            if (forceRefresh) {
                return fromNetwork;
            } else {
                return Observable.concat(fromCache, fromNetwork).first();
            }
        }
    }
    
    

    几个参数注释上面已经写得很清楚了,不需要过多的解释。这里我们先取了一个Observable<T>对象fromCache,里面的操作很简单,去缓存里面找个key对应的缓存,如果有就发射数据。在fromNetwork里面做的操作仅仅是缓存数据这一操作。最后判断如果强制刷新就直接返回fromNetwork反之用Observable.concat()做一个合并。concat操作符将多个Observable结合成一个Observable并发射数据。这里又用了first()fromCachefromNetwork任何一步一旦发射数据后面的操作都不执行。

    最后我们新建一个HttpUtil用来返回用户关心的数据,缓存,显示Dialog在这里面进行。

    public class HttpUtil{
        /**
         * 构造方法私有
         */
        private HttpUtil() {
        }
    
        /**
         * 在访问HttpUtil时创建单例
         */
        private static class SingletonHolder {
            private static final HttpUtil INSTANCE = new HttpUtil();
        }
    
        /**
         * 获取单例
         */
        public static HttpUtil getInstance() {
            return SingletonHolder.INSTANCE;
        }
    
        //添加线程管理并订阅
        public void toSubscribe(Observable ob, final ProgressSubscriber subscriber,String cacheKey,boolean isSave, boolean forceRefresh) {
            //数据预处理
            Observable.Transformer<HttpResult<Object>, Object> result = RxHelper.handleResult();
              //重用操作符
            Observable observable = ob.compose(result)
                    .doOnSubscribe(new Action0() {
                        @Override
                        public void call() {
                            //显示Dialog和一些其他操作
                            subscriber.showProgressDialog();
                        }
                    });
            //缓存
            RetrofitCache.load(cacheKey,observable,isSave,forceRefresh).subscribe(subscriber);
    
    
    
        }
    
    

    Activity生命周期管理

    基本的网络请求都是向服务器请求数据,客户端拿到数据后更新UI。但也不排除意外情况,比如请求回数据途中Activity已经不在了,这个时候就应该取消网络请求。
    要实现上面的功能其实很简单,两部分

    • 随时监听Activity(Fragment)的生命周期并对外发射出去; 在我们的网络请求中,接收生命周期
    • 并进行判断,如果该生命周期是自己绑定的,如Destory,那么就断开数据向下传递的过程

    实现以上功能需要用到RxjavaSubject的子类PublishSubject
    在你的BaseActivity中添加如下代码

    public class BaseActivity extends AppCompatActivity {
    
        public final PublishSubject<ActivityLifeCycleEvent> lifecycleSubject = PublishSubject.create();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            lifecycleSubject.onNext(ActivityLifeCycleEvent.CREATE);
            super.onCreate(savedInstanceState);
        }
    
        @Override
        protected void onPause() {
            lifecycleSubject.onNext(ActivityLifeCycleEvent.PAUSE);
            super.onPause();
        }
    
        @Override
        protected void onStop() {
            lifecycleSubject.onNext(ActivityLifeCycleEvent.STOP);
            super.onStop();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            lifecycleSubject.onNext(ActivityLifeCycleEvent.DESTROY);
        }
    
    

    这样的话,我们把所有生命周期事件都传给了PublishSubject了,或者说PublishSubject已经接收到了并能够对外发射各种生命周期事件的能力了。

    现在我们要让网络请求的时候去监听这个PublishSubject,在收到相应的生命周期后取消网络请求,这又用到了我们神奇的compose(),我们需要修改handleResult代码如下

    public static <T> Observable.Transformer<HttpResult<T>, T> handleResult(final ActivityLifeCycleEvent event,final PublishSubject<ActivityLifeCycleEvent> lifecycleSubject) {
            return new Observable.Transformer<HttpResult<T>, T>() {
                @Override
                public Observable<T> call(Observable<HttpResult<T>> tObservable) {
                    Observable<ActivityLifeCycleEvent> compareLifecycleObservable =
                            lifecycleSubject.takeFirst(new Func1<ActivityLifeCycleEvent, Boolean>() {
                                @Override
                                public Boolean call(ActivityLifeCycleEvent activityLifeCycleEvent) {
                                    return activityLifeCycleEvent.equals(event);
                                }
                            });
                    return tObservable.flatMap(new Func1<HttpResult<T>, Observable<T>>() {
                        @Override
                        public Observable<T> call(HttpResult<T> result) {
                            if (result.getCount() != 0) {
                                return createData(result.getSubjects());
                            } else {
                                return Observable.error(new ApiException(result.getCount()));
                            }
                        }
                    }) .takeUntil(compareLifecycleObservable).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).subscribeOn(AndroidSchedulers.mainThread()).observeOn(AndroidSchedulers.mainThread());
                }
            };
        }
    
    

    调用的时候增加了两个参数一个是ActivityLifeCycleEvent 其实就是一些枚举表示Activity的生命周期

    
    public enum  ActivityLifeCycleEvent {
        CREATE,
        START,
        RESUME,
        PAUSE,
        STOP,
        DESTROY
    }
    

    另外一个参数就是我们在BaseActivity添加的PublishSubject,这里用到了takeUntil()它的作用是监听我们创建的compareLifecycleObservablecompareLifecycleObservable中就是判断了如果当前生命周期和Activity一样就发射数据,一旦compareLifecycleObservable 对外发射了数据,就自动把当前的Observable(也就是网络请求的Observable)停掉。
    当然有个库是专门针对这种情况的,叫RxLifecycle,不过要继承他自己的RxActivity,当然这个库不只是针对网络请求,其他所有的Rxjava都可以。有需要的可以去看看。

    最后新建一个ApiService存放我们的请求

    public interface ApiService {
        @GET("/student/mobileRegister")
        Observable<HttpResult<UserEntity>> login(@Query("phone") String phone, @Query("password") String psw);
    
    }
    
    
    使用

    使用起来就超级简单了

    
    /**
     *
     *
     //  ┏┓   ┏┓
     //┏┛┻━━━┛┻┓
     //┃       ┃
     //┃   ━   ┃
     //┃ ┳┛ ┗┳ ┃
     //┃       ┃
     //┃   ┻   ┃
     //┃       ┃
     //┗━┓   ┏━┛
      //   ┃   ┃   神兽保佑
      //   ┃   ┃   阿弥陀佛
      //   ┃   ┗━━━┓
      //   ┃       ┣┓
      //   ┃       ┏┛
      //   ┗┓┓┏━┳┓┏┛
      //     ┃┫┫ ┃┫┫
      //     ┗┻┛ ┗┻┛
      //
      */
    
          //获取豆瓣电影TOP 100
            Observable ob = Api.getDefault().getTopMovie(0, 100);
            HttpUtil.getInstance().toSubscribe(ob, new ProgressSubscriber<List<Subject>>(this) {
                @Override
                protected void _onError(String message) {
                    Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();
                }
    
                @Override
                protected void _onNext(List<Subject> list) {
                    
                }
    
            }, "cacheKey", ActivityLifeCycleEvent.PAUSE, lifecycleSubject, false, false);
    
    

    具体很多东西都可以在使用的时候具体修改,比如缓存我用的HawkDialog是我自己定义的一个SimpleLoadDialog。源码已经给出请多指教!

    -------------更新--------------
    评论区有人提出对于Activity生命周期的管理,个人疏忽大意,特地来加上。

    END!

    Thanks
    Rx处理服务器请求、缓存的完美封装
    给 Android 开发者的 RxJava 详解
    RxJava 与 Retrofit 结合的最佳实践
    可能是东半球最全的RxJava使用场景小结
    带你学开源项目:RxLifecycle - 当Activity被destory时自动暂停网络请求

    相关文章

      网友评论

      • TMAC_EAH:https://www.jianshu.com/p/fbbe4b027e19

        厚个脸皮推荐下我的

        最起码getinstance我觉得可以抽一抽方法
      • a3f4fdbfbb5e:createData方法 onNext出现异常了 catch快的onError方法不执行 在rxjava2里 什么情况
      • Stephen__Li:httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);

        DEFAULT_TIMEOUT = 10000?你确定不是10?
      • tmyzh:TimeUnit.MILLISECONDS吧 超时时间略长 嘻嘻
      • 广成de微博:不是链式啊
      • TonyStake:大神,源码有吗?
        _小河马:https://github.com/Hemumu/RxSample
      • 崔老板:和我们的项目封装的一模一样:flushed:
        崔老板:互粉啊 https://github.com/icuihai
      • 903d18a7bd21:大兄弟还在吗 ,我以前用的你这个 用的挺好,最近在升级rx2时候,按照此来,最后发现,在绑定生命周期时,rx1的CompositeSubscription到了rx2变成了CompositeDisposable,这时候我不知道怎么添加订阅关系了,不知道能否解答下,参数是Disposable,可是我想添加的是Observer,本来就应该是添加Observer吧?有点蒙蔽 ...
        903d18a7bd21:@_小河马 不知道现在呢?我看了所有的 只喜欢你这种方式。可惜现在还没出rx2的
        903d18a7bd21:@_小河马 :+1: :+1: :+1:
        _小河马:@Xiccc 我好久没写android了,大兄弟,最近转后台了。rx2还没研究过呢
      • 903d18a7bd21:很帅啊,有rx2吗?这个我已经使用过一次了 但是现在都用rx2 问下马哥有更新吗 我学习下 或者说rx1升级rx2 有什么需要注意的地方 看你handleResult主要是那块
      • 259c1c37b7cc:你好,想问下,每次请求之前都要进行String->RequestBody,传递json参数给服务端,这个部分怎么封装比较好呢?
      • 2d0ac5a90df7:RxJava2中用什么代替lifecycleSubject.takeFirst中的takeFirst操作符呀?
        3b13bb76002a:@_小河马 我下载了,还是1的啊。。。
        _小河马:@鸡鸡复鸡鸡 最新版已经更新到RxJava2了
        3b13bb76002a:请问用哪个方法替换?
      • RedLi: 马哥。有时间加上一个webservice的解析,soap 的,请求体和返回体都是xml的解析,这样就支持两种格式的网络请求了:relieved:
      • 刚刚了然:为什么不用okhttp自带的缓存拦截器呢
        我一定会学会:ok自带的拦截缓存有局限,只能get而且还需要服务器配合,小伙子!
      • def294ec8e5b:大神,我有个疑问,关于缓存问题的. 如果我服务器数据变了,缓存数据还是不变,那我界面上显示的肯定还是老数据.怎么搞?
        _小河马:@屌丝程序猿丶是的
        def294ec8e5b:@_小河马 马大哥,如果我要达到你所说的效果,是不是必须把强制刷新置为true:stuck_out_tongue_closed_eyes:
        _小河马:缓存是你进入页面先读取缓存显示然后再去网络请求数据,如果请求到了数据那么更新缓存,没有请求到则给出相应的提示
      • 帝乙岩:我将代码基于Rxjava2+Retrofit2进行修改,有两个问题:1.doOnSubscribe中调用dialog会导致请求失败,还在查找原因.2.缓存使用Hawk,当false时cache为null,直接调用onCompleted中断了请求,导致请求失败(可能这块没有理解)。
        帝乙岩:@MelvinCen takeFirst操作符弃用使用filter(new Predicate).take(1);takeUntil应该是可以正常使用。
        2d0ac5a90df7:你换RxJava2的时候takeFirst操作符和takeUntil那里怎么操作的哦现在?求指教
        刚刚了然:rxjava2调用onNext(null),只要是null,就会异常。
      • 3cf4933d1229:一直报Hawk is not built. Please call build() and wait the initialisation finishes错误
        代码一样的,,不知道为啥,求解
        _小河马:@开着拖拉机迎接春天 是的
        3cf4933d1229:@_小河马 可了谢谢,上传和下载要自己封装是吧
        _小河马:@开着拖拉机迎接春天 初始化Hawk
      • 3cf4933d1229:为什么我在Fragment里面Dialog Show不出来
      • izheer:总结的很好!首先,参考别人的成果无可厚非,但是呢,更希望把参考的出处标明出来,毕竟有别人的成果,作者说是不是?
      • 3e566325ea53:请问大神 怎么处理分页加载的数据
      • 南先森不知道的事: if (result.getCount() != 0) {
        return createData(result.getSubjects());
        } else {
        return Observable.error(new ApiException(result.getCount()));
        }
        为什么if 和 else都走了
      • 风雪武者_3867:demo在哪下载啊
      • 笑叹红颜美:博主,请问下上传和下载代码该怎么实现??
      • 王元_Trump:有空换成rxjava就好了
      • 光源_Android:请教一下,有没有考虑过无返回值的请求怎么封装处理?比如上传文件请求,返回数据里只有 {"code":0,"desc":"success" }, 那 HttpResult<T> 就不能用了。
        24K纯帅豆:T可以是Void
      • Dinos:博主,为什么需要
        .subscribeOn(Schedulers.io())
        .unsubscribeOn(Schedulers.io())
        .subscribeOn(AndroidSchedulers.mainThread())
        .observeOn(AndroidSchedulers.mainThread())
        直接使用subscrieOn(Schedulers.io);和observeOn(AndroidSchedulers.mainThread);不行吗?为什么需要绑定解绑,在绑定在解绑呢?
        Dinos:@_小河马 我看了上面你介绍的抛物线大神的,和东半球的那个,都没有说明unsubscribeOn的作用,求解答,谢谢啦=_=
        Dinos:@_小河马 大神谢了,我能在问一个问题吗?你在上面写的ob.compose(result).doOnSubscribe,其中,doOnSubscribe后面并没有再次调用subscribeOn方法来切换线程,也就是doOnSubscribe方法使用的是默认的线程吗?还有就是,指定解除绑定在那个线程,有什么用吗?即使我使用了unsubscribeOn(Schedulers.io())方法,我发现后面的链式调用仍然是在原来的子线程中运行,即使我再次使用subscribeOn这个操作符,也还是子线程
        _小河马:@Dinos 这里的unsubscribeOn不是解绑的意思,是指定解除绑定在哪个线程发生
      • Dinos:我从郭神,那边过来的=_=,一直收藏着,到现在才有时间看,学到了很多,谢谢啦
      • HannyYeung:我想转载啊!!
        _小河马:可以转载,请注明出处就可以啦!
      • huangyirui:你好,我刚学rxjava,请问使用rxjava怎么异步调用腾讯的上传额
      • 小宋敲码:关于生命周期那一段不错!受教了!!!
      • steven123:感觉楼主说的put缓存的地方是在主线程中,尽管注释说:这里的fromNetwork 不需要指定Schedule,在handleRequest中已经变换了
      • 21fe789340c5:缓存那一块学习了,非常感谢
      • 012f8d530b41:rxjava 使用的是gson解析,如果我使用的是fastjson解析,怎么解决?
        _小河马:Retrofit 默认没有提供fastjson的Converter,你可以自定义一个解析fastjson的Converter。github上有例子!
      • 衣忌破:缓存应该可以在Interceptor那 作处理吧
      • 5bc8369b98dd:楼主是否没有考虑缓存过期?
        _小河马:@feng04030 缓存那一块儿的逻辑是自己处理的。我只是给个例子参考
      • HaRun:很好,学习了!
      • 0873a2acb861:运行报错:Error:Could not download conceal.aar (com.facebook.conceal:conceal:1.1.2): No cached version available for offline mode 请大神指教!还有,该框架能上传多张图片吗?谢谢大神,在线等! :sunglasses:
        0873a2acb861:@_小河马 大神,我在等着你的完善呀:smile:
        _小河马:@呦呦大梵王 目前还没有加入上传功能,正在完善!
      • 164b5ecd311b:你好,正常我们是用token来验证是否过期,来后做重新的登录的操作。但是我现在遇到的情况是我用public class HttpResult<T> {
        private int code;
        private String message;
        private T data;
        }
        这个类code来判断是否登录失效。所以是在Observable.compose()根据返回的code进行判断。
        if(code==“失效的code值”)
        {
        ???
        }
        如何进行写才能进行后台登录,然后继续调用之前的接口。
        a3f4fdbfbb5e:哥们 这个功能你实现了么
        _小河马:@gnmmd 如果code==“失效的code值”就调用return Observable.error(new ApiException(失效的code值)); 错误code和错误信息自己定义就可以了
        164b5ecd311b:@gnmmd 补充:如果使用接口回调进行操作,就会打破链式结构。
      • Mr_默:哈哈,连请求地址都跟tough1985哥们写的一模一样。我表示你这个基本都是照着他的写的,不要狡辩哦 :smile:
        _小河马:@Mr_默 加了很多东西哦!这叫站在巨人的肩膀上
      • 8e47b82fb2be:可以给个 SimpleLoadDialog 源码的传送门吗,想做这样一个东西到苦于一直没有什么好的思路
        _小河马:@小二哥的幸福生活 源码里面有一个简单的,可以参考下
      • icoo:RxHelper类61行代码return activityLifeCycleEvent.equals(event); debug时,没有执行到。请问61行这句写了 又没执行到,有何意义?
        _小河马:@icoo http://android.jobbole.com/83847/ 去看看这篇文章吧!
        icoo:@_小河马 toSubscribe这个方法,我随便传一个生命周期,都能获得到数据,怎么叫网络请求取消呢?例子中是传了ActivityLifeCycleEvent.DESTROY,是啥意思?
        _小河马:@icoo 如果当前Activity的生命周期equals你传入的生命周期,利用takeFirst发射数据,其实就是取消网络请求。
      • Allen_tong:github上例子有bug吧 加载不出来数据
        _小河马:@Allen_tong 获取100个很慢请求超时了所以会请求失败。可以改少一点10个左右就行!
        Allen_tong:@icoo 哦 可以了 之前看rxjava retrofit完美封装那篇 HttpMethods.getInstance().getTopMovie(new ProgressSubscriber(getTopMovieOnNext, MainActivity.this), 0, 10); end到10 估计作者也没注意到100加载不出来
        icoo:@Allen_tong 一开始我试用这个例子也加载不出数据,你把代码稍微修改下,base_url 改成http://
        获取电影不要取100了,改成 Observable ob = Api.getDefault().getTopMovie(0, 30); 就可以获得数据了
      • icoo:下载了例子,为什么加载不出数据?
      • 02a55645ba1d:RxJava 新手表示,对分页数据怎么处理,如:{"status":0,"total":232,"data":[{},{},{}],数据处理后只返回了数据集合,没有总条数,就没办法判断还有没有更多数据
      • f54a4988d387:为什么在请求数据的时候,要点击两次物理返回键才能消失dialog,点击返回键一次还仍然在请求数据
      • 孙福生微博:给你一个赞
      • 067d829266d9:缓存部分为什么不用okhttp?
      • 372b2af34a47:这个网络的取消应该是可以用在图片的滑动加载上的吧😁
      • 汪简书:RxJava新手表示很满足。问下 Hawk 能不使用么,直接去掉对代码可有影响啊
        _小河马:@然而喵星人早已看穿了一切 没影响,就是不缓存了
      • JJoom:subscribeOn不是只能使用一次吗
        Dinos:@_小河马 但是不是只有第一个起作用吗?这样调用了两次,和不写有什么区别吗?
        _小河马:@JJoom 当使用了多个 subscribeOn() 的时候,只有第一个 subscribeOn() 起作用。这里调用两次是因为要指定doOnSubscribe()的执行线程。doOnSubscribe是执行离他最近的subscribeOn()。
      • 490422f5d58d:感谢分享,学习学习
      • w4lr:Mark
      • woitaylor:加上周期问题就完美了
        _小河马:@woitaylor 已更新
      • Android之路:写的很好,但是好像没考虑生命周期的问题.
        _小河马:@追风917 Rxlifecycle吧 :wink:
        妙法莲花1234: @_小河马 感谢,生命周期 RxlifeCicle 好像是这个库
        _小河马:@Android之路 :smile: 谢谢提醒

      本文标题:Android RxJava+Retrofit完美封装(缓存,请

      本文链接:https://www.haomeiwen.com/subject/uihxpttx.html