上篇文章 我們瞭解到Retrofit+RxJava 和一個簡單的將訂閱者Subscriber傳入Activity的封裝,而這一篇,我們從相同格式的Http請求數據如何封裝談起
相同格式的Http請求數據該如何封裝
public class HttpResult<T> {
private int resultCode;
private String resultMessage;
private T data;
}
如果data是一個User對象的話,那麼在定義Service方法的返回值就可以寫爲:Observable<HttpResult<User>>
在上面的示例中,我也創建了一個HttpResult類,用來模仿這個形式,將其中的Subject dna單獨封裝了起來
public class HttpResult<T> {
//用來模仿resultCode和resultMessage
private int count;
private int start;
private int total;
private String title;
//用來模仿Data
private T subjects;
}
這樣泛型就要寫爲:Observable<HttpResult<List<Subject>>>
相同格式的Http請求數據統一進行預處理
{
"resultCode": 0,
"resultMessage": "成功",
"data": {}
}
我們想要對resultCode和resultMessage先做一個判斷,因爲如果resultCode
== 0
代表success,那麼resultCode
!= 0
時data一般都是null。
Activity或Fragment對resultCode和resultMessage基本沒有興趣,他們只對請求狀態和data數據感興趣。
基於這種考慮,我們在resultCode != 0
的時候,拋出個自定義的ApiException。這樣就會進入到subscriber的onError中,我們可以在onError中處理錯誤信息。
另外,請求成功時,需要將data數據轉換爲目標數據類型傳遞給subscriber,因爲,Activity和Fragment只想拿到和他們真正相關的數據。
使用Observable的map方法可以完成這一功能。
在HttpMethods中創建一個內部類HttpResultFunc,代碼如下: (這個可以實現兩兩類型裝換嗎)/**
* 用來統一處理Http的resultCode,並將HttpResult的Data部分剝離出來返回給subscriber
*
* @param <T> Subscriber真正需要的數據類型,也就是Data部分的數據類型
*/
private class HttpResultFunc<T> implements Func1<HttpResult<T>, T>{
@Override
public T call(HttpResult<T> httpResult) {
if (httpResult.getResultCode() != 0) {
throw new ApiException(httpResult.getResultCode());
}
return httpResult.getData();
}
}
然後我們的getMovice方法該改改爲:
public void getTopMovie(Subscriber<List<Subject>> subscriber, int start, int count){
movieService.getTopMovie(start, count)
.map(new HttpResultFunc<List<Subject>>())
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
由於HttpResult中的泛型T就是我們希望傳遞給subscriber的數據類型,而數據可以通過httpResult的getData方法獲得,這樣我們就處理了泛型問題,錯誤處理問題,還有將請求數據部分剝離出來給subscriber這樣我們只需要關注Data數據的類型,而不必在關心整個過程了。
HttpResult<User>
//or
HttpResult<List<Subject>>
而在定義Subscriber的時候泛型是 java User //or List<Subject>不然你會得到一個轉型錯誤。
如何取消一個Http請求
爲什麼會提到Observer?
public final Subscription subscribe(final Observer<? super T> observer) {
if (observer instanceof Subscriber) {
return subscribe((Subscriber<? super T>)observer);
}
return subscribe(new Subscriber<T>() {
@Override
public void onCompleted() {
observer.onCompleted();
}
@Override
public void onError(Throwable e) {
observer.onError(e);
}
@Override
public void onNext(T t) {
observer.onNext(t);
}
});
}
但是後來發現了問題, ,由於Observer沒有unsubscribe方法, 故無法取消, 爲了後面,我們還是選擇使用Subscriber一個需要ProgressDialog的Subscriber該有的樣子
我們希望有一個Subscriber在我們每次發送請求的時候能夠彈出一個ProgressDialog,然後在請求接受的時候讓這個ProgressDialog消失,同時在我們取消這個ProgressDialog的同時能夠取消當前的請求,而我們只需要處理裏面的數據就可以了。我們先來創建一個類,就叫ProgressSubscriber,讓他繼承Subscriber。
Subscriber給我們提供了onStart、onNext、onError、onCompleted四個方法。
其中只有onNext方法返回了數據,那我們自然希望能夠在onNext裏面處理數據相關的邏輯。
onStart方法我們用來啓動一個ProgressDialog。 onError方法我們集中處理錯誤,同時也停止ProgressDialog onComplated方法裏面停止ProgressDialog
其中我們需要解決兩個問題
處理onNext
我們先來定義一個接口,命名SubscriberOnNextListener
public interface SubscriberOnNextListener<T> {
void onNext(T t);
}
代碼很簡單。再來看一下ProgressSubscriber現在的代碼
public class ProgressSubscriber<T> extends Subscriber<T> {
private SubscriberOnNextListener mSubscriberOnNextListener;
private Context context;
public ProgressSubscriber(SubscriberOnNextListener mSubscriberOnNextListener, Context context) {
this.mSubscriberOnNextListener = mSubscriberOnNextListener;
this.context = context;
}
@Override
public void onStart() {
}
@Override
public void onCompleted() {
Toast.makeText(context, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Throwable e) {
Toast.makeText(context, "error:" + e.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(T t) {
mSubscriberOnNextListener.onNext(t);
}
}
MainActivity使用是這樣的:
先來定義一個SubscriberOnNextListener對象,可以在onCreate裏面創建這個對象
private SubscriberOnNextListener getTopMovieOnNext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
getTopMovieOnNext = new SubscriberOnNextListener<List<Subject>>() {
@Override
public void onNext(List<Subject> subjects) {
resultTV.setText(subjects.toString());
}
};
}
private void getMovie(){
HttpMethods.getInstance().getTopMovie(
new ProgressSubscriber(getTopMovieOnNext, MainActivity.this),
0, 10);
}
這樣Activity或Fragment就只需要關注拿到結果之後的邏輯了,其他的完全不用操心。處理ProgressDialog
public interface ProgressCancelListener {
void onCancelProgress();
}
然後我們用ProgressSubscriber來實現這個接口,這樣ProgressSubscriber就有了一個onCancelProgress方法,在這裏面取消訂閱。@Override
public void onCancelProgress() {
if (!this.isUnsubscribed()) {
this.unsubscribe();
}
}
然後我用了一個Handler來封裝了ProgressDialog。
public class ProgressDialogHandler extends Handler {
public static final int SHOW_PROGRESS_DIALOG = 1;
public static final int DISMISS_PROGRESS_DIALOG = 2;
private ProgressDialog pd;
private Context context;
private boolean cancelable;
private ProgressCancelListener mProgressCancelListener;
public ProgressDialogHandler(Context context, ProgressCancelListener mProgressCancelListener,
boolean cancelable) {
super();
this.context = context;
this.mProgressCancelListener = mProgressCancelListener;
this.cancelable = cancelable;
}
private void initProgressDialog(){
if (pd == null) {
pd = new ProgressDialog(context);
pd.setCancelable(cancelable);
if (cancelable) {
pd.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
mProgressCancelListener.onCancelProgress();
}
});
}
if (!pd.isShowing()) {
pd.show();
}
}
}
private void dismissProgressDialog(){
if (pd != null) {
pd.dismiss();
pd = null;
}
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_PROGRESS_DIALOG:
initProgressDialog();
break;
case DISMISS_PROGRESS_DIALOG:
dismissProgressDialog();
break;
}
}
}
Handler接收兩個消息來控制顯示Dialog還是關閉Dialog。 創建Handler的時候我們需要傳入ProgressCancelListener的對象實例。
最後貼出ProgressSubscriber的完整代碼:
public class ProgressSubscriber<T> extends Subscriber<T> implements ProgressCancelListener{
private SubscriberOnNextListener mSubscriberOnNextListener;
private ProgressDialogHandler mProgressDialogHandler;
private Context context;
public ProgressSubscriber(SubscriberOnNextListener mSubscriberOnNextListener, Context context) {
this.mSubscriberOnNextListener = mSubscriberOnNextListener;
this.context = context;
mProgressDialogHandler = new ProgressDialogHandler(context, this, true);
}
private void showProgressDialog(){
if (mProgressDialogHandler != null) {
mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget();
}
}
private void dismissProgressDialog(){
if (mProgressDialogHandler != null) {
mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget();
mProgressDialogHandler = null;
}
}
@Override
public void onStart() {
showProgressDialog();
}
@Override
public void onCompleted() {
dismissProgressDialog();
Toast.makeText(context, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Throwable e) {
dismissProgressDialog();
Toast.makeText(context, "error:" + e.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(T t) {
mSubscriberOnNextListener.onNext(t);
}
@Override
public void onCancelProgress() {
if (!this.isUnsubscribed()) {
this.unsubscribe();
}
}
}
整個封裝完成, 如果你覺得寫更改線程的代碼覺得也很煩的話,可以把訂閱這部分也封裝起來:
public void getTopMovie(Subscriber<List<Subject>> subscriber, int start, int count){
//原來的樣子
// movieService.getTopMovie(start, count)
// .map(new HttpResultFunc<List<Subject>>())
// .subscribeOn(Schedulers.io())
// .unsubscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(subscriber);
//修改之後的樣子
Observable observable = movieService.getTopMovie(start, count)
.map(new HttpResultFunc<List<Subject>>());
toSubscribe(observable, subscriber);
}
//添加線程管理並訂閱
private void toSubscribe(Observable o, Subscriber s){
o.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s);
}