天天看点

Retrofit+Rxjava实现嵌套逻辑的链式调用

最近做app有一个需求,service的某个接口(B接口)调用很慢,所以不能频繁的调用,然后service就想了一个逻辑:先返回一个调用速度快的接口(A接口),里面有一个字段,一旦这个字段发生了改变,再去调用第二个接口(B接口)。我们app这边的逻辑也很简单,先把A接口调用返回的值用sharedPreference存到本地,然后每次调用A接口的时候都去对比一下本地的值,要是相等就说明不需要调用B接口,要是不相等就要调用!

然后,我以最快的速度,按照之前的套路写完了

1,先写2个model类对应接口A和接口B

2,解析两个接口的数据。(Gson+okhttpfinal(封装okhttp))

3,写逻辑,先调用A,里面嵌套B

然后写完了,我突然想到,好像可以用Retrofit+Rxjava试试,因为我个人是比较反感嵌套的,尤其是波浪形到吗,剪不断理还乱的逻辑。

(友情提示:这篇文章适合已经熟悉简单的Retrofit+Rxjava模式调用接口的同学,要是不熟悉,建议先收藏本文,去熟悉一下,再回来看,你会获益匪浅。这套流程是我们国外的一个开发写的,我稍加整理,贡献给大家。)

第一步 :创建每个接口的model类

名字是

接口A的model:AppdataVersionEntity

接口B的model:AppdataDownLoadEntity

这个地方代码就不给了,很简单,如果使用gson解析json,建议使用GsonFormat插件,很方便。

第二步 :封装Retrofit

1、按照套路,创建一个类来对Retrofit进行简单的封装

public RESTClient(Context context) {
        this.context = context;

        Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("")//这个地方一般放你的域名,所有的接口都有这种格式
        .client(provideOkHttpClient(context))//自定义client,如果不需要可以用默认的。                                       .addConverterFactory(GsonConverterFactory.create())//这一步就省去了手动解析json数据了。
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();

        restAPI = retrofit.create(RestAPI.class);//创建
    }
           

2、再来看上面说到的自定义client。

private OkHttpClient provideOkHttpClient(final Context context) {
        //抓log的,要在app的build.gradle的dependencies里面compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        File httpCacheDirectory = new File(context.getCacheDir(), "responses");//创建缓存文件
        Cache cache = new Cache(httpCacheDirectory,  *  * );//设置缓存10M
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);//log的等级,4种等级,这是最详细的一种等级
        OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
                .connectTimeout( * , TimeUnit.MILLISECONDS)//超时时间
                .readTimeout( * , TimeUnit.MILLISECONDS)//超时时间
                .addInterceptor(new Interceptor() {//添加拦截器
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        HttpUrl httpUrl = request.url().newBuilder()
                        //这个地方的addQueryParameter是所有接口都附加的两个值,因各家app而异,加到这个地方就省去了,在retrofit里面单独添加的麻烦。
                                .addQueryParameter("v", "1.0.3")
                                .addQueryParameter("client","1")
                                .build();
                        request = request.newBuilder().url(httpUrl).build();
                        Response response = chain.proceed(request);
                        Log.d("Response Code", response.code() + "");
                        if (response.code() == ) {//这个地方可以根据返回码做一些事情。通过sendBroadcast发出去。
                            Intent intent = new Intent("Logout");
                            intent.putExtra("badAuth", true);
                            context.sendBroadcast(intent);
                        }
                        return response;
                    }
                })
                .addInterceptor(loggingInterceptor)//把上面的log拦截器添加进来
                .cache(cache)//添加缓存
                .build();//build生效
        return okHttpClient;//返回client
    }
           

3、在这个类中,添加一个接口,来封装需要调用的接口

public interface RestAPI {
        @FormUrlEncoded
        @POST("接口A")
        Observable<AppdataVersionEntity> getAppdataVersion();
        @FormUrlEncoded
        @POST("接口B")
        Observable<AppdataDownLoadEntity> getAppdataDownLoad();
    }
           

然后再这个类中创建一个对外开放的方法调用接口

private RestAPI restAPI;
   public RestAPI getRestAPI() {
        return restAPI;
    }
           

第三步:调用接口

经过上面的封装,我们可以按照逻辑,调用接口了。逻辑已经在文章的开头说过了,这里再说一遍:有一个需求,service的某个接口(B接口)调用很慢,所以不能频繁的调用,然后service就想了一个逻辑:先返回一个调用速度快的接口(A接口),里面有一个字段,一旦这个字段发生了改变,再去调用第二个接口(B接口)。我们app这边的逻辑也很简单,先把A接口调用返回的值用sharedPreference存到本地,然后每次调用A接口的时候都去对比一下本地的值,要是相等就说明不需要调用B接口,要是不相等就要调用!

RESTClient client = new RESTClient(homeActivity);
        client.getRestAPI().getAppdataVersion()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<AppdataVersionEntity>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                    }

                    @Override
                    public void onNext(AppdataVersionEntity appdataVersionEntity) {//在调用第一个接口之后调用第二个接口,这里出现了嵌套
                        version = appdataVersionEntity.getVersion()+"";
                        String localVersion =  getLocalAppVersion(homeActivity);
                        if(){//"本地版本发生变化"
                        //生变化的逻辑,要去调用B接口
                            client .getRestAPI().getAppdataDownLoad()
                                    .subscribeOn(Schedulers.io())
                                    .observeOn(AndroidSchedulers.mainThread())
                                    .subscribe(new Observer<AppdataDownLoadEntity>() {
                                        @Override
                                        public void onCompleted() {
                                        }

                                        @Override
                                        public void onError(Throwable e) {
                                        }

                                        @Override
                                        public void onNext(AppdataDownLoadEntity appdataDownLoadEntity) {
                                           //调用了B接口,做一些逻辑
                                        }
                                    });
                        }else{
                            //没发生变化的逻辑
                        }
                    }
                });
           

上面的伪代码大体一看就行,想表达一个意思是,逻辑上要嵌套调用接口,这时候Retrofit+Rxjava也出现了嵌套,这种前台是违背了Jack设计Retrofit的初衷的,肯定是不建议的。怎么优化?用flatMap!往下看。

(要是你对observeOn和subscribeOn的关系不理解,请看我的这篇文章)

RESTClient client = new RESTClient(homeActivity);
         client .getRestAPI()
                .getAppdataVersion()
                .flatMap(new Func1<AppdataVersionEntity, Observable<AppdataDownLoadEntity>>() {//调用第一个接口(A接口)**注释1**
                    @Override
                    public Observable<AppdataDownLoadEntity> call(AppdataVersionEntity appdataVersionEntity) {
                        if(){
                            //如果发生了改变,就去调用接口B
                            return  client.getRestAPI().getAppdataDownLoad();
                        }else{
                            //如果没发生改变,做一些逻辑
                        }
                        return null;//如果return null就继续向下执行,并不会crash
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<AppdataDownLoadEntity>() {
                    @Override
                    public void onNext(AppdataDownLoadEntity appdataDownLoadEntity) {
                       //这是执行接口B,做一些逻辑
                    }
                    @Override
                    public void onCompleted() {
                        JLogUtils.i("robin3","onCompleted");
                    }

                    @Override
                    public void onError(Throwable e) {
                        e.printStackTrace();
                    }

                });
           

好了,看看这代码,左边的 ## . ## 完全对其,再也不用看大波浪了,心情愉悦。最重要的是这对后面改这块代码的人很友好。

上面有一个我加粗的注释1,我想解释一下,这个地方可能会发生问题,一定要穿对泛型,否则会报错,这个泛型是什么意思呢?这要看Fun1函数的参数了,第一个是接口A的model类型(AppdataVersionEntity),第二个参数是你要去请求的B接口 ( Observable)。所以这个地方你应该也就明白了flatMap的作用:进去A,出来B。具体到项目就是,穿进去一个String,出来一个他的hashmap,传进一个userId,出来一个包含userId的model类,传一个boolean,出一个Observable。

有说的不对的,不理解的欢迎在下面指出!