這套框架來源於現有專案,這幾天開了新專案正好用到順手就把這套框架抽出來了,也方便以後使用。目前網上對Rxjava2+Retrofit2的封裝真是太多了,但是大體思路都是一樣的,而且好多程式碼都具有相似性,這套其實也不例外,大家可選擇性使用。
首先我們先不說封裝思路,先說說這套框架都都具有哪些功能及如何使用;
功能
1 使用RxCache快取機制,可自定義快取過期時間,及資料分頁快取等功能。
2 統一的請求錯誤處理;
3 統一的網路狀態判斷處理;
4 基於HttpLoggingInterceptor的請求日誌列印。
以上就是這套框架可以實現的功能,框架中並沒有像其他的一樣封裝了ProgressBar
,因為每個專案不同,ProgressBar的樣式需求並不一樣,就算同一個專案中下拉重新整理和普通載入可能也不一樣,所以需要使用的小夥伴自己定義ProgressBar。
此外這套框架使用了RxCache實現快取,而並不是通過OKHttp快取,所以這套框架對伺服器沒有任何要求,不需要定義好Header之類的東西。如果你對RxCache不熟悉,可以看看這篇文章,或者RxCache的官網。不知道這麼牛的框架為啥start目前才1000多呢,不說廢話了,來看使用方式。
使用方式
呼叫以下程式碼完成網路請求
//宣告監聽
HttpSubscriber mHttpObserver =new HttpSubscriber(new OnResultCallBack<TestBean>() {
@Override
public void onSuccess(TestBean tb) {
}
@Override
public void onError(int code,String errorMsg) {
}
});
//發起請求
HttpManager.getInstance().getDatas(mHttpObserver,1,10,"json",true);
//取消請求
mHttpObserver.unSubscribe();複製程式碼
看起來是不是很簡單?
下面來說說上面的程式碼是如何完成網路資料請求的。先來看看這個框架的結構
僅僅只有七個類而已。
簡單介紹一下個各類的職責
ApiResponse——封裝的返回資料模板(裡面的error_code,reason,result名稱需要你跟後臺的小夥伴對應好,通常情況下error_code程式碼狀態碼,reason為成功或失敗的提示資訊,result中為具體的資料,由於資料格式未知所以使用泛型代表)
ApiService——Retrofit的資料請求介面。注意一下每個方法的返回值型別。(我們真正需要的是TestBean中的資料它必須被ApiResponse包裝,最後返回Observable型別)
CacheProvider——RxCache的快取介面,注意它的第一個引數型別必須和Retrofit資料請求介面的返回值型別一樣。
OnResultCallBack——請求成功或失敗的回撥。
ApiException——公共的請求錯誤處理類。
HttpSubscriber——公共的請求訂閱者。
HttpManager——發起請求的管理類。
首先是引入的各種庫檔案
//Rxjava2
compile 'io.reactivex.rxjava2:rxjava:2.0.7'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
//Retrofit2
compile 'com.squareup.retrofit2:retrofit:latest.release'
compile 'com.squareup.retrofit2:converter-gson:latest.release'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
//RxCache
compile "com.github.VictorAlbertos.RxCache:runtime:1.8.0-2.x"
compile 'com.github.VictorAlbertos.Jolyglot:gson:0.0.3'
//Okhttp-interceptor
compile 'com.squareup.okhttp3:logging-interceptor:3.6.0'複製程式碼
HttpManager的初始化
public static HttpManager getInstance() {
if (instance == null) {
synchronized (HttpManager.class) {
if (instance == null) {
instance = new HttpManager();
}
}
}
return instance;
}複製程式碼
單例模式的HttpManager,來看HttpManager的建構函式
private HttpManager() {
HttpLoggingInterceptor.Level level= HttpLoggingInterceptor.Level.BODY;
HttpLoggingInterceptor loggingInterceptor=new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.i("HttpManager",message);
}
});
loggingInterceptor.setLevel(level);
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addInterceptor(loggingInterceptor);
OkHttpClient okHttpClient = builder.build();
mRetrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(Constant.BASE_URL)
.client(okHttpClient)
.build();
cacheProvider = new RxCache.Builder()
.persistence(mContext.getFilesDir(), new GsonSpeaker())
.using(CacheProvider.class);
mApiService = mRetrofit.create(ApiService.class);
}複製程式碼
在建構函式中,首先我們通過HttpLoggingInterceptor設定了攔截器,並通過addInterceptor方法設定給 OkHttpClient.Builder。然後是構建OkHttpClient.Builder及okHttpClient ,再然後是Retrofit的初始化。
下面接著是RxCache的初始化,並在其中設定了快取目錄,mContext為ApplicationContext,由init方法傳入。
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
HttpManager.init(this);//不做任何操作僅僅是快取一下Application引用
}
}複製程式碼
那麼HttpManager是如何完成一個網路請求的呢?
1 ApiService中宣告請求介面
@FormUrlEncoded
@POST("query?key=7c2d1da3b8634a2b9fe8848c3a9edcba")
Observable<ApiResponse<TestBean>> getDatas(@Field("pno") int pno, @Field("ps") int ps, @Field("dtype") String dtype);複製程式碼
2CacheProvider中宣告快取介面(如果需要快取就寫)
@LifeCache(duration = 5, timeUnit = TimeUnit.MINUTES)
Observable<ApiResponse<TestBean>> getDatas(Observable<ApiResponse<TestBean>> oRepos, EvictProvider evictDynamicKey);複製程式碼
注意看註解,可以自定義快取過期時間。(EvictProvider 引數是設定是否需要對請求的資料進行快取,具體可以看這裡)同時,第一個引數一定要是Observable
3 HttpManager宣告對應的請求方法
方法有兩種,帶快取的方式
public void getDatasWithCache(Observer<TestBean> subscriber, int pno, int ps, String dtype, boolean update) {
toSubscribe(cacheProvider.getDatas(mApiService.getDatas(pno, ps,dtype),new EvictProvider(update)), subscriber);
}複製程式碼
不帶快取的方式
public void getDatasNoCache(Observer<TestBean> subscriber, int pno, int ps, String dtype) {
toSubscribe(mApiService.getDatas(pno, ps,dtype), subscriber);
}複製程式碼
看到沒不帶快取的方法只是沒有用cacheProvider.getDatas()方法包裹,同時少了一個控制是否更新的引數update。
再來看一下toSubscribe()方法的實現
private <T> void toSubscribe(Observable<ApiResponse<T>> o, Observer<T> s) {
o.subscribeOn(Schedulers.io())
.map(new Function<ApiResponse<T>, T>() {
@Override
public T apply(@NonNull ApiResponse<T> response) throws Exception {
int code=Integer.parseInt(response.getCode());
if (code!=Constant.SUCCESS_CODE) {
throw new ApiException(code, response.getMsg());
} else {
return response.getDatas();
}
}
})
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s);
}複製程式碼
藉助Rxjava的map方法完成資料的進一步轉換,注意SUCCESS_CODE,這個值是資料返回成功時的狀態碼,需要你和後臺小夥伴定義好,一般情況下都為0。如果code!=SUCCESS_CODE,那麼出錯返回的code的和message都會拋給ApiException,在ApiException中的getApiExceptionMessage方法你可以根據具體code重新定義不同的message,或者使用傳入進來的message,通常情況下這個類不需要修改,如果需要客戶端根據code自定義message那麼就按照上面所的方式修改即可。最後所有的message都會拋給HttpSubscriber的onError方法
@Override
public void onError(Throwable e) {
if (e instanceof CompositeException) {
CompositeException compositeE=(CompositeException)e;
for (Throwable throwable : compositeE.getExceptions()) {
if (throwable instanceof SocketTimeoutException) {
mOnResultListener.onError(ApiException.Code_TimeOut,ApiException.SOCKET_TIMEOUT_EXCEPTION);
} else if (throwable instanceof ConnectException) {
mOnResultListener.onError(ApiException.Code_UnConnected,ApiException.CONNECT_EXCEPTION);
} else if (throwable instanceof UnknownHostException) {
mOnResultListener.onError(ApiException.Code_UnConnected,ApiException.CONNECT_EXCEPTION);
} else if (throwable instanceof RxCacheException) {
//快取異常暫時不做處理
} else if (throwable instanceof MalformedJsonException) {
mOnResultListener.onError(ApiException.Code_MalformedJson,ApiException.MALFORMED_JSON_EXCEPTION);
}
}
}else {
String msg = e.getMessage();
int code;
if (msg.contains("#")) {
code = Integer.parseInt(msg.split("#")[0]);
mOnResultListener.onError(code, msg.split("#")[1]);
} else {
code = ApiException.Code_Default;
mOnResultListener.onError(code, msg);
}
}
}複製程式碼
在這個方法中也統一處理了網路問題。注意看不同的網路狀態返回的狀態碼是不一樣的。
SocketTimeoutException 網路超時 1000
ConnectException 連結異常 1001
UnknownHostException Host異常 1001
MalformedJsonException 解析異常 1020
其他錯誤資訊返回來自伺服器的error code,這裡的"#"是在ApiException中的getApiExceptionMessage()方法拼接的。
最終執行到
mHttpObserver =new HttpSubscriber(new OnResultCallBack<TestBean>() {
@Override
public void onSuccess(TestBean tb) {
}
@Override
public void onError(int code,String errorMsg) {
}
});複製程式碼
中的onError方法,在這裡我們根據不同的code展示不同的介面即可(例如常見的網路錯誤介面),或者通過Toast等其他方式給使用者提示。
再回到toSubscribe方法,如果資料返回成功了,即code==SUCCESS_CODE,那麼資料會返回給HttpSubscriber的onNext方法
@Override
public void onNext(T t) {
if (mOnResultListener != null) {
mOnResultListener.onSuccess(t);
}
}複製程式碼
onNext中呼叫OnResultCallBack的onSuccess方法,把資料傳遞到
mHttpObserver =new HttpSubscriber(new OnResultCallBack<TestBean>() {
@Override
public void onSuccess(TestBean tb) {
}
@Override
public void onError(int code,String errorMsg) {
}
});複製程式碼
中的onSuccess中,在這裡我們把資料展示給使用者即可。
HttpSubscriber的另一項任務就是取消資料請求操作。
在onSubscribe中通過一個全域性的mDisposable記錄當前的Disposable
@Override
public void onSubscribe(Disposable d) {
mDisposable = d;
}複製程式碼
在需要取消的地方呼叫unSubscribe()即可
public void unSubscribe() {
if (mDisposable != null && !mDisposable.isDisposed()) {
mDisposable.dispose();
}
}複製程式碼
下面執行一下程式
mHttpObserver =new HttpSubscriber(new OnResultCallBack<TestBean>() {
@Override
public void onSuccess(TestBean tb) {
for (TestBean.ListBean bean : tb.getList()) {
result+=bean.toString();
}
resultTv.setText(result);
}
@Override
public void onError(int code,String errorMsg) {
resultTv.setText("onError: code:"+code+" errorMsg:"+errorMsg );
}
});複製程式碼
這裡成功後通過TextView顯示一下資料,失敗時也顯示一下錯誤資訊和code;
首先是不使用快取
HttpManager.getInstance().getDatasNoCache(mHttpObserver,1,10,"json");複製程式碼
資料請求成功,再來測試斷網情況
斷網後返回了錯誤碼1003。
下面測試快取
設定一個5分鐘的快取
@LifeCache(duration = 5, timeUnit = TimeUnit.MINUTES)
Observable<ApiResponse<TestBean>> getDatas(Observable<ApiResponse<TestBean>> oRepos, EvictProvider evictDynamicKey);
HttpManager.getInstance().getDatasWithCache(mHttpObserver,1,10,"json",false);複製程式碼
可以看到第一次請求產生了網路流量,說明資料來自網路,而後的幾次請求均沒有產生流量,說明資料來自本地快取。
好了這套框架就介紹到這裡吧,有需要的可以在這裡下載
github.com/shiweibsw/E…
如何使用?
1 build.gradle中匯入外掛
2 將http包下所有內容拷貝到你的工程即可。
下一步的計劃
1 retrofit 介面類封裝基本的get和post請求;
2 支援以外掛的形式匯入工程。
掘金技術徵文juejin.im/post/58d8e9…