Rxjava+Retrofit统一异常处理与生命周期管理
一、引入
9102年了,终于准备用mvp来重构一下了
之前写过Mvc模式下的Retrofit统一异常处理,这次用MVP重构过程中发现诸多不足之处,便重新进行修缮,使其在我看来更加优雅emmmmmm,文中不足之处,还望诸位同仁多多指点。
推荐阅读:终于有人把 【移动开发】 从基础到实战的全套视频弄全了
二、基本流程描述
BaseView
BaseView接口定义了可能用到的方法,特别是addSubscribe,用来管理RxJava生命周期。
1public interface BaseView {
2
3 /**
4 * 显示吐司
5 *
6 * @param msg 提示消息
7 */
8 void showMsg(String msg);
9
10 /**
11 * 显示加载动画
12 */
13 void showProgress();
14
15 /**
16 * 显示提示
17 */
18 void showTip(@QMUITipDialog.Builder.IconType int iconType, CharSequence tipWord);
19
20 /**
21 * 关闭加载动画
22 */
23 void hideProgress();
24
25 /**
26 * 关闭提示
27 */
28 void hideTip();
29
30 /**
31 * 跳转页面
32 */
33 void startActivitySample(Class<?> cls);
34
35 /**
36 * Rx事件管理
37 *
38 * @param subscription
39 */
40 void addSubscribe(Disposable subscription);
41
42}
BasePresenter
BasePresenter方法中只定义了绑定View与解绑View的接口
1public interface BasePresenter<T extends BaseView> {
2
3 void attachView(T view);
4
5 void detachView();
6}
BaseActivity/Fragment
这个类是封装了一些常用方法,并且实现了BaseView的全部接口。 并且预留了两个用于mvp模式的空方法
1@Override
2protected void onCreate(@Nullable Bundle savedInstanceState) {
3 super.onCreate(savedInstanceState);
4 initContentView(R.layout.activity_base);
5 setContentView(getLayout());
6 mTopBar = (QMUITopBar) findViewById(R.id.base_topbar);
7 ButterKnife.bind(this);
8 mContext = this;
9 mSwipeBackLayout = getSwipeBackLayout();
10 if (isStartSwipeBack()) {
11 mSwipeBackLayout.setEdgeTrackingEnabled(SwipeBackLayout.EDGE_LEFT);
12 } else {
13 mSwipeBackLayout.setEnableGesture(false);
14 }
15 AppManager.addActivity(this);
16 // 在此处调用绑定Presenter方法
17 initPresenter();
18 initEventAndData();
19}
20
21@Override
22protected void onDestroy() {
23 unSubscribe();
24 removePresenter();
25 AppManager.removeActivity(this);
26 super.onDestroy();
27}
28
29protected void initPresenter() {
30
31}
32
33protected void removePresenter() {
34
35}
BaseMvpActivity/Fragment
实现了BaseActivity/Fragment中的预留方法
1public abstract class BaseMvpActivity<T extends BasePresenter> extends BaseActivity {
2
3 protected T mPresenter;
4
5 @Override
6 protected void initPresenter() {
7 mPresenter = createPresenter();
8 if (mPresenter != null) {
9 mPresenter.attachView(this);
10 }
11 }
12
13 @Override
14 protected void removePresenter() {
15 if (mPresenter != null) {
16 mPresenter.detachView();
17 }
18 }
19
20 /**
21 * 创建Presenter
22 *
23 * @return
24 */
25 protected abstract T createPresenter();
26
27}
SamplePresenter
SamplePresenter实现了BasePresenter中的绑定view与解绑view
BaseResponse
RESTful API的基类,看下代码很容易明白,但是需要注意下isOk(BaseView view)方法
1public class BaseResponse<T> {
2
3
4 private int statusCode;
5 private String message;
6 private T data;
7
8 public boolean isOk(BaseView view) {
9 // statusCode == 1服务器请求成功
10 if (statusCode == 1) {
11 return true;
12 } else {
13 // 服务器正常请求返回的错误
14 NetworkError.error(view, new ServerException(statusCode, message));
15 return false;
16 }
17 }
18
19 // get/set...
20}
NetworkError
根据不同的标志统一处理异常以及服务器返回的错误
1public class NetworkError {
2
3 public static void error(BaseView view, Throwable throwable) {
4 RetrofitException.ResponeThrowable responeThrowable = RetrofitException.retrofitException(throwable);
5 // 此处可以通过判断错误代码来实现根据不同的错误代码做出相应的反应
6 switch (responeThrowable.code) {
7 case RetrofitException.ERROR.UNKNOWN:
8 case RetrofitException.ERROR.PARSE_ERROR:
9 case RetrofitException.ERROR.NETWORD_ERROR:
10 case RetrofitException.ERROR.HTTP_ERROR:
11 case RetrofitException.ERROR.SSL_ERROR:
12 view.showMsg(responeThrowable.message);
13 break;
14 case -1:
15 // 跳转到登陆页面
16 view.startActivitySample(LoginActivity.class);
17 break;
18 default:
19 view.showMsg(responeThrowable.message);
20 break;
21 }
22 }
23}
ServerException
自定义服务器异常
1public class ServerException extends RuntimeException {
2
3 public int code;
4
5 public ServerException(int code, String message) {
6 super(message);
7 this.code = code;
8 }
9}
RetrofitException
自定义网络异常,获取错误原因
1public class RetrofitException {
2
3 private static final int UNAUTHORIZED = 401;
4 private static final int FORBIDDEN = 403;
5 private static final int NOT_FOUND = 404;
6 private static final int REQUEST_TIMEOUT = 408;
7 private static final int INTERNAL_SERVER_ERROR = 500;
8 private static final int BAD_GATEWAY = 502;
9 private static final int SERVICE_UNAVAILABLE = 503;
10 private static final int GATEWAY_TIMEOUT = 504;
11
12 public static ResponeThrowable retrofitException(Throwable e) {
13 ResponeThrowable ex;
14 if (e instanceof HttpException) {
15 HttpException httpException = (HttpException) e;
16 ex = new ResponeThrowable(e, ERROR.HTTP_ERROR);
17 switch (httpException.code()) {
18 case UNAUTHORIZED:
19 case FORBIDDEN:
20 case NOT_FOUND:
21 case REQUEST_TIMEOUT:
22 case GATEWAY_TIMEOUT:
23 case INTERNAL_SERVER_ERROR:
24 case BAD_GATEWAY:
25 case SERVICE_UNAVAILABLE:
26 default:
27 ex.message = "网络错误";
28 break;
29 }
30 return ex;
31 } else if (e instanceof ServerException) {
32 // 服务器下发的错误
33 ServerException resultException = (ServerException) e;
34 ex = new ResponeThrowable(resultException, resultException.code);
35 ex.message = resultException.getMessage();
36 return ex;
37 } else if (e instanceof JsonParseException
38 || e instanceof JSONException
39 || e instanceof ParseException) {
40 ex = new ResponeThrowable(e, ERROR.PARSE_ERROR);
41 ex.message = "解析错误";
42 return ex;
43 } else if (e instanceof ConnectException
44 || e instanceof SocketTimeoutException
45 || e instanceof UnknownHostException) {
46 ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);
47 ex.message = "连接失败";
48 return ex;
49 } else if (e instanceof SSLHandshakeException) {
50 ex = new ResponeThrowable(e, ERROR.SSL_ERROR);
51 ex.message = "证书验证失败";
52 return ex;
53 } else {
54 ex = new ResponeThrowable(e, ERROR.UNKNOWN);
55 ex.message = "未知错误";
56 return ex;
57 }
58 }
59
60
61 /**
62 * 约定异常
63 */
64 class ERROR {
65 /**
66 * 未知错误
67 */
68 public static final int UNKNOWN = 1000;
69 /**
70 * 解析错误
71 */
72 public static final int PARSE_ERROR = 1001;
73 /**
74 * 网络错误
75 */
76 public static final int NETWORD_ERROR = 1002;
77 /**
78 * 协议出错
79 */
80 public static final int HTTP_ERROR = 1003;
81 /**
82 * 证书出错
83 */
84 public static final int SSL_ERROR = 1005;
85 }
86
87 public static class ResponeThrowable extends Exception {
88 public int code;
89 public String message;
90
91 public ResponeThrowable(Throwable throwable, int code) {
92 super(throwable);
93 this.code = code;
94 }
95 }
96}
RetrofitClient
使用单例封装的Retrofit,这里就不写了,相信大家都写过
ServiceApi
这个是Api接口与10对应,这里用的Observable,感觉没必要用Flowable,用Flowable的话下面12这个就不是这么写了
1public interface ServiceApi {
2
3 /**
4 * 测试接口
5 */
6 @POST("test")
7 Observable<BaseResponse<LoginModel>> login();
8}
1RetrofitSubscriber(Observer)
通过BaseView调用在BaseActivity/Fragment中实现的addSubscribe将Disposable添加到CompositeDisposable中,在页面销毁时先中断请求,以免造成view销毁了还去调用导致空指针异常。并且根据Observer的接口通过BaseView来处理加载动画(在BaseActivity/Fragment中实现)。
1public abstract class RetrofitSubscriber<T> implements Observer<T> {
2
3 private final WeakReference<BaseView> mView;
4
5 public RetrofitSubscriber(BaseView view) {
6 super();
7 mView = new WeakReference<>(view);
8 }
9
10 @Override
11 public void onSubscribe(Disposable d) {
12 if (!NetworkUtils.isConnected()) {
13 mView.get().showMsg("网络未连接,请检查网络");
14 d.dispose();
15 } else {
16 mView.get().showProgress();
17 mView.get().addSubscribe(d);
18 }
19 }
20
21 @Override
22 public void onComplete() {
23 if (mView != null && mView.get() != null) {
24 mView.get().hideProgress();
25 }
26 }
27
28 @Override
29 public void onError(Throwable e) {
30 if (mView != null && mView.get() != null) {
31 mView.get().hideProgress();
32 }
33 onNetError(e);
34 }
35
36 @Override
37 public void onNext(T response) {
38 if (response instanceof BaseResponse) {
39 if (((BaseResponse) response).isOk(mView.get())) {
40 onSuccess(response);
41 } else {
42 onServiceError(response);
43 }
44 } else {
45 onOtherSuccess(response);
46 }
47 }
48
49 /**
50 * 请求成功并且服务器未下发异常
51 *
52 * @param response
53 */
54 protected abstract void onSuccess(T response);
55
56 /**
57 * 请求成功, 返回非继承自BaseResponse的非标准Bean或字符串
58 *
59 * @param response
60 */
61 protected void onOtherSuccess(T response) {
62
63 }
64
65 /**
66 * 请求成功,服务器下发异常
67 *
68 * @param response
69 */
70 protected void onServiceError(T response) {
71
72 }
73
74 /**
75 * 网络异常
76 *
77 * @param e
78 */
79 protected void onNetError(Throwable e) {
80 if (mView != null && mView.get() != null) {
81 NetworkError.error(mView.get(), e);
82 }
83 }
84}
放一张我画的流程图,比较魔性
三、使用示例
Contract
1public interface LoginContract {
2
3 interface View extends BaseView {
4
5 /**
6 * 登陆成功
7 * @param loginModel
8 */
9 void loginSuccess(LoginModel loginModel);
10
11 }
12
13 interface Presenter extends BasePresenter<View> {
14
15 /**
16 * 登陆
17 */
18 void login(String userName, String pwd);
19 }
20}
21
22Activity
23
24public class LoginActivity extends BaseMvpActivity<LoginPresenter> implements LoginContract.View {
25
26 @BindView(R.id.et_login_user)
27 ClearEditText mEtLoginUser;
28 @BindView(R.id.et_login_password)
29 ClearEditText mEtLoginPassword;
30
31 @Override
32 protected int getLayout() {
33 return R.layout.activity_login;
34 }
35
36 @Override
37 protected void initEventAndData() {
38 initView();
39 }
40
41 @Override
42 protected LoginPresenter createPresenter() {
43 return new LoginPresenter();
44 }
45
46 private void initView() {
47 // ...
48 }
49
50 private void toLogin() {
51 mPresenter.login(mEtLoginUser.getText().toString(), mEtLoginPassword.getText().toString();
52 }
53
54 @Override
55 public void loginSuccess(LoginModel loginModel) {
56 startActivity(new Intent(mContext, MainActivity.class));
57 finish();
58 }
59
60 @OnClick({R.id.tv_login_submit})
61 public void onViewClicked(View view) {
62 switch (view.getId()) {
63 case R.id.tv_login_submit:
64 //上传用户名和密码的方法
65 toLogin();
66 break;
67 }
68 }
69
70}
Presenter
1public class LoginPresenter extends SamplePresenter<LoginContract.View> implements LoginContract.Presenter {
2
3 @Override
4 public void login(String userName, String pwd) {
5 RetrofitClient
6 .getInstance()
7 .gService
8 .login()
9 .compose(RxSchedulersUtils.rxObservableSchedulerHelper())
10 .subscribe(new RetrofitSubscriber<BaseResponse<LoginModel>>(mView) {
11 @Override
12 protected void onSuccess(BaseResponse<LoginModel> response) {
13 mView.loginSuccess(response.getData());
14 }
15 });
16 }
17}
三、源码
https://github.com/sdwfqin/AndroidQuick/tree/2.x/app/src/main/java/com/sdwfqin/quickseed
-
mvpretrofit是本文对应的代码
-
retrofit是mvc模式下对应的代码