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, 10 * 1024 * 1024);//設定快取10M
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);//log的等級,4種等級,這是最詳細的一種等級
OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
.connectTimeout(60 * 1000, TimeUnit.MILLISECONDS)//超時時間
.readTimeout(60 * 1000, 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() == 401) {//這個地方可以根據返回碼做一些事情。通過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。
有說的不對的,不理解的歡迎在下面指出!