RxJava + Retrofit2.0的專案實戰完美封裝
Retrofit 和RxJava已經出來很久了,從去年開始rxjava和retrofit就開始火,所以之前在做專案的時候也用了rxjava和retrofit,今天就介紹一下在專案中如何封裝rxjava和retrofit。對於 RxJava 不是很瞭解的同學推薦你們看這篇文章給 Android 開發者的 RxJava 詳解。Retrofit的使用可以看看Android Retrofit 2.0使用。
首先在我們的工程的build.gradle中新增依賴:
compile 'io.reactivex:rxjava:1.1.8'
compile 'io.reactivex:rxandroid:1.2.1'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
然後封裝retrofit:
public class HttpMethods {
//介面根地址
public static final String BASE_URL = "http://www.baidu.com";
//設定超時時間
private static final long DEFAULT_TIMEOUT = 10_000L;
private Retrofit retrofit;
private OkHttpClient client;
private static class SingletonHolder {
private static final HttpMethods INSTANCE = new HttpMethods();
}
//私有化構造方法
private HttpMethods() {
client = new OkHttpClient.Builder()
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
//新增請求頭
//.addInterceptor(new HeaderInterceptor())
//新增日誌列印攔截器
.addInterceptor(new LoggerInterceptor("===", true))
.build();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
//新增Gson解析
.addConverterFactory(GsonConverterFactory.create())
//新增rxjava
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
public static HttpMethods getInstance() {
return SingletonHolder.INSTANCE;
}
//這裡返回一個泛型類,主要返回的是定義的介面類
public <T> T createService(Class<T> clazz) {
return retrofit.create(clazz);
}
}
這就是定義的介面類:
/**
* 介面定義
*/
public interface ApiService {
@FormUrlEncoded
@POST("/sys/sendMsg")
Observable<BaseEntity<DataEntity>> getData(@FieldMap Map<String, String> params);
}
這裡的根地址和介面中的地址都是隨便寫的一個地址,用的時候替換成自己專案的地址就行了。新增請求頭的話,由於專案中沒用到所以直接註釋了。
接下來就是封裝伺服器請求和返回資料。一般情況下返回的資料結構是這樣的:
{
"status_code":10000,
"error_msg":"請求成功!",
"data":{
"name":"張三",
"age":3
}
}
如果你們的伺服器返回不是這樣的格式那你就只有坐下來請他喝茶,跟他好好說了。大不了就懟他。
對於這樣的資料我們肯定要對status_code做出一些判斷,不同的status_code對應不同的錯誤資訊。所以我們新建一個BaseEntity,對應上面的資料結構。
public class BaseEntity<T> implements Serializable {
private int status_code;
private String error_msg;
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public int getStatus_code() {
return status_code;
}
public void setStatus_code(int status_code) {
this.status_code = status_code;
}
public String getError_msg() {
return error_msg;
}
public void setError_msg(String error_msg) {
this.error_msg = error_msg;
}
}
這就是所有實體的一個基類,data可以為任何資料型別,所以我們使用泛型。
我們要對所以返回結果進行預處理,新建一個DefaultTransformer繼承Observable.Transformer,預處理無非就是對status_code進行判斷和解析,不同的錯誤返回不同的錯誤資訊。有個操作符compose。因為我們在每一個請求中都會處理status_code以及使用一些操作符,比如用observeOn和subscribeOn來切換執行緒。RxJava提供了一種解決方案:Transformer(轉換器),一般情況下就是通過使用操作符compose()來實現。
程式碼:
public class DefaultTransformer<T> implements Observable.Transformer<T, T> {
@Override
public Observable<T> call(Observable<T> tObservable) {
return tObservable
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1<T, T>() {// 通用錯誤處理,判斷code
@Override
public T call(T t) {
if (((BaseEntity<T>)t).getStatus_code() != 10000) {
throw new ApiException(((BaseEntity<T>)t).getStatus_code(), ((BaseEntity<T>)t).getError_msg());
}
return t;
}
});
}
public static <T> DefaultTransformer<T> create() {
return new DefaultTransformer<>();
}
}
這裡我們使用map操作符把Obserable< BaseEntity< T > >,轉換成為Observable< T >在內部對status_code進行了預處理。這裡當狀態碼不等於10000就表示請求出錯丟擲異常。這裡的ApiException是我們自定義的一個異常類,用來處理伺服器返回的異常。
程式碼:
public class ApiException extends IllegalArgumentException {
private int code;
public ApiException(int code, String msg) {
super(msg);
this.code = code;
}
public int getCode() {
return code;
}
}
接下來就是處理網路請求的操作,和顯示載入等待的dialog:
public abstract class ApiSubscriber<T> extends Subscriber<T> {
private LoadingDialog mDialog;
public ApiSubscriber() {
}
public ApiSubscriber(@NonNull Context context) {
mDialog = new LoadingDialog(context);
}
@Override
public void onStart() {
if (mDialog != null)
mDialog.show();
}
@Override
public void onCompleted() {
if (mDialog != null && mDialog.isShowing())
mDialog.dismiss();
}
/**
* 只要鏈式呼叫中丟擲了異常都會走這個回撥
*/
@Override
public void onError(Throwable e) {
if (mDialog != null && mDialog.isShowing())
mDialog.dismiss();
if (e instanceof ApiException) {
//處理伺服器返回的錯誤
} else if (e instanceof ConnectException || e instanceof UnknownHostException) {
ToastUtils.showShort("網路異常,請檢查網路");
} else if (e instanceof TimeoutException || e instanceof SocketTimeoutException) {
ToastUtils.showShort("網路不暢,請稍後再試!");
} else if (e instanceof JsonSyntaxException) {
ToastUtils.showShort("資料解析異常");
} else {
ToastUtils.showShort("服務端錯誤");
}
e.printStackTrace();
}
}
這裡新建一個ApiSubscriber類繼承Subscriber類,寫了兩個構造方法,一個是顯示dialog,一個是不顯示dialog。重寫了三個方法,這裡我們沒有重寫onNext方法,因為這個方法是請求成功返回資料的,所以我們等到請求資料介面去重寫。在onError裡面做了所有的錯誤處理,在裡面可以根據伺服器返回的錯誤碼對不同的錯誤做不同的處理。
接下來的話就是管理生命週期了。有個專門的庫可以管理生命週期的叫RxLifecycle,可以去看看。不過我們不用這個,我們在BaseActivity裡面寫。
public abstract class BaseActivity extends AppCompatActivity {
private CompositeSubscription mCompositeSubscription;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void onDestroy() {
super.onDestroy();
onUnsubscribe();
}
/**
* 所有rx訂閱後,需要呼叫此方法,用於在detachView時取消訂閱
*/
protected void addSubscription(Subscription subscribe) {
if (mCompositeSubscription == null)
mCompositeSubscription = new CompositeSubscription();
mCompositeSubscription.add(subscribe);
}
/**
* 取消本頁面所有訂閱
*/
protected void onUnsubscribe() {
if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
mCompositeSubscription.unsubscribe();
}
}
}
我們在請求資料的地方呼叫addSubscription(Subscription subscribe)方法,當activity在onDestroy()的時候就取消訂閱了。
最後就是我們的請求資料了,這是之前定義的一個偽介面:
public interface ApiService {
@FormUrlEncoded
@POST("/sys/sendMsg")
Observable<BaseEntity<DataEntity>> getData(@FieldMap Map<String, String> params);
}
然後我們在activity裡面這樣寫:
public class MainActivity extends BaseActivity {
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv_content);
findViewById(R.id.btn_request).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getData();
}
});
}
private void getData() {
Map<String, String> params = new HashMap<>();
params.put("user_name", "");
params.put("user_pwd", "");
Subscription subscribe = HttpMethods.getInstance()
.createService(ApiService.class)
.getData(params)
.compose(DefaultTransformer.<BaseEntity<DataEntity>>create())
.subscribe(new ApiSubscriber<BaseEntity<DataEntity>>(this) {
@Override
public void onNext(BaseEntity<DataEntity> entity) {
}
});
addSubscription(subscribe);//新增訂閱
}
}
在onNext方法裡面就能獲取到我們的資料了,當然這個介面是請求不成功的,在使用的時候替換成自己的介面就行了。retrofit還有很多的註解,這裡只是使用了@FormUrlEncoded註解(表單的形式)做個示例。到這裡封裝就基本完成了,我也是菜鳥,希望大家多提意見,互相學習。