上篇文章 我们了解到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);
}