天天看點

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。

有說的不對的,不了解的歡迎在下面指出!