0%

安卓MVP模式下的Rxjava+Retrofit统一异常处理与生命周期管理

一、引入

9102年了,终于准备用mvp来重构一下了

之前写过Mvc模式下的Retrofit统一异常处理,这次用MVP重构过程中发现诸多不足之处,便重新进行修缮,使其在我看来更加优雅emmmmmm,文中不足之处,还望诸位同仁多多指点。

二、基本流程描述

  1. BaseView

    BaseView接口定义了可能用到的方法,特别是addSubscribe,用来管理RxJava生命周期。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public interface BaseView {

    /**
    * 显示吐司
    *
    * @param msg 提示消息
    */
    void showMsg(String msg);

    /**
    * 显示加载动画
    */
    void showProgress();

    /**
    * 显示提示
    */
    void showTip(@QMUITipDialog.Builder.IconType int iconType, CharSequence tipWord);

    /**
    * 关闭加载动画
    */
    void hideProgress();

    /**
    * 关闭提示
    */
    void hideTip();

    /**
    * 跳转页面
    */
    void startActivitySample(Class<?> cls);

    /**
    * Rx事件管理
    *
    * @param subscription
    */
    void addSubscribe(Disposable subscription);

    }
  2. BasePresenter

    BasePresenter方法中只定义了绑定View与解绑View的接口

    1
    2
    3
    4
    5
    6
    public interface BasePresenter<T extends BaseView> {

    void attachView(T view);

    void detachView();
    }
  3. BaseActivity/Fragment

    这个类是封装了一些常用方法,并且实现了BaseView的全部接口。
    并且预留了两个用于mvp模式的空方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    initContentView(R.layout.activity_base);
    setContentView(getLayout());
    mTopBar = (QMUITopBar) findViewById(R.id.base_topbar);
    ButterKnife.bind(this);
    mContext = this;
    mSwipeBackLayout = getSwipeBackLayout();
    if (isStartSwipeBack()) {
    mSwipeBackLayout.setEdgeTrackingEnabled(SwipeBackLayout.EDGE_LEFT);
    } else {
    mSwipeBackLayout.setEnableGesture(false);
    }
    AppManager.addActivity(this);
    // 在此处调用绑定Presenter方法
    initPresenter();
    initEventAndData();
    }

    @Override
    protected void onDestroy() {
    unSubscribe();
    removePresenter();
    AppManager.removeActivity(this);
    super.onDestroy();
    }

    protected void initPresenter() {

    }

    protected void removePresenter() {

    }
  4. BaseMvpActivity/Fragment

    实现了BaseActivity/Fragment中的预留方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public abstract class BaseMvpActivity<T extends BasePresenter> extends BaseActivity {

    protected T mPresenter;

    @Override
    protected void initPresenter() {
    mPresenter = createPresenter();
    if (mPresenter != null) {
    mPresenter.attachView(this);
    }
    }

    @Override
    protected void removePresenter() {
    if (mPresenter != null) {
    mPresenter.detachView();
    }
    }

    /**
    * 创建Presenter
    *
    * @return
    */
    protected abstract T createPresenter();

    }
  5. SamplePresenter

    SamplePresenter实现了BasePresenter中的绑定view与解绑view

  6. BaseResponse

    RESTful API的基类,看下代码很容易明白,但是需要注意下isOk(BaseView view)方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class BaseResponse<T> {


    private int statusCode;
    private String message;
    private T data;

    public boolean isOk(BaseView view) {
    // statusCode == 1服务器请求成功
    if (statusCode == 1) {
    return true;
    } else {
    // 服务器正常请求返回的错误
    NetworkError.error(view, new ServerException(statusCode, message));
    return false;
    }
    }

    // get/set...
    }
  7. NetworkError

    根据不同的标志统一处理异常以及服务器返回的错误

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class NetworkError {

    public static void error(BaseView view, Throwable throwable) {
    RetrofitException.ResponeThrowable responeThrowable = RetrofitException.retrofitException(throwable);
    // 此处可以通过判断错误代码来实现根据不同的错误代码做出相应的反应
    switch (responeThrowable.code) {
    case RetrofitException.ERROR.UNKNOWN:
    case RetrofitException.ERROR.PARSE_ERROR:
    case RetrofitException.ERROR.NETWORD_ERROR:
    case RetrofitException.ERROR.HTTP_ERROR:
    case RetrofitException.ERROR.SSL_ERROR:
    view.showMsg(responeThrowable.message);
    break;
    case -1:
    // 跳转到登陆页面
    view.startActivitySample(LoginActivity.class);
    break;
    default:
    view.showMsg(responeThrowable.message);
    break;
    }
    }
    }
  8. ServerException

    自定义服务器异常

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class ServerException extends RuntimeException {

    public int code;

    public ServerException(int code, String message) {
    super(message);
    this.code = code;
    }
    }
  9. RetrofitException

    自定义网络异常,获取错误原因

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    public class RetrofitException {

    private static final int UNAUTHORIZED = 401;
    private static final int FORBIDDEN = 403;
    private static final int NOT_FOUND = 404;
    private static final int REQUEST_TIMEOUT = 408;
    private static final int INTERNAL_SERVER_ERROR = 500;
    private static final int BAD_GATEWAY = 502;
    private static final int SERVICE_UNAVAILABLE = 503;
    private static final int GATEWAY_TIMEOUT = 504;

    public static ResponeThrowable retrofitException(Throwable e) {
    ResponeThrowable ex;
    if (e instanceof HttpException) {
    HttpException httpException = (HttpException) e;
    ex = new ResponeThrowable(e, ERROR.HTTP_ERROR);
    switch (httpException.code()) {
    case UNAUTHORIZED:
    case FORBIDDEN:
    case NOT_FOUND:
    case REQUEST_TIMEOUT:
    case GATEWAY_TIMEOUT:
    case INTERNAL_SERVER_ERROR:
    case BAD_GATEWAY:
    case SERVICE_UNAVAILABLE:
    default:
    ex.message = "网络错误";
    break;
    }
    return ex;
    } else if (e instanceof ServerException) {
    // 服务器下发的错误
    ServerException resultException = (ServerException) e;
    ex = new ResponeThrowable(resultException, resultException.code);
    ex.message = resultException.getMessage();
    return ex;
    } else if (e instanceof JsonParseException
    || e instanceof JSONException
    || e instanceof ParseException) {
    ex = new ResponeThrowable(e, ERROR.PARSE_ERROR);
    ex.message = "解析错误";
    return ex;
    } else if (e instanceof ConnectException
    || e instanceof SocketTimeoutException
    || e instanceof UnknownHostException) {
    ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);
    ex.message = "连接失败";
    return ex;
    } else if (e instanceof SSLHandshakeException) {
    ex = new ResponeThrowable(e, ERROR.SSL_ERROR);
    ex.message = "证书验证失败";
    return ex;
    } else {
    ex = new ResponeThrowable(e, ERROR.UNKNOWN);
    ex.message = "未知错误";
    return ex;
    }
    }


    /**
    * 约定异常
    */
    class ERROR {
    /**
    * 未知错误
    */
    public static final int UNKNOWN = 1000;
    /**
    * 解析错误
    */
    public static final int PARSE_ERROR = 1001;
    /**
    * 网络错误
    */
    public static final int NETWORD_ERROR = 1002;
    /**
    * 协议出错
    */
    public static final int HTTP_ERROR = 1003;
    /**
    * 证书出错
    */
    public static final int SSL_ERROR = 1005;
    }

    public static class ResponeThrowable extends Exception {
    public int code;
    public String message;

    public ResponeThrowable(Throwable throwable, int code) {
    super(throwable);
    this.code = code;
    }
    }
    }
  10. RetrofitClient

    使用单例封装的Retrofit,这里就不写了,相信大家都写过

  11. ServiceApi

    这个是Api接口与10对应,这里用的Observable,感觉没必要用Flowable,用Flowable的话下面12这个就不是这么写了

    1
    2
    3
    4
    5
    6
    7
    8
    public interface ServiceApi {

    /**
    * 测试接口
    */
    @POST("test")
    Observable<BaseResponse<LoginModel>> login();
    }
  12. RetrofitSubscriber(Observer)

    通过BaseView调用在BaseActivity/Fragment中实现的addSubscribeDisposable添加到CompositeDisposable中,在页面销毁时先中断请求,以免造成view销毁了还去调用导致空指针异常。并且根据Observer的接口通过BaseView来处理加载动画(在BaseActivity/Fragment中实现)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    public abstract class RetrofitSubscriber<T> implements Observer<T> {

    private final WeakReference<BaseView> mView;

    public RetrofitSubscriber(BaseView view) {
    super();
    mView = new WeakReference<>(view);
    }

    @Override
    public void onSubscribe(Disposable d) {
    if (!NetworkUtils.isConnected()) {
    mView.get().showMsg("网络未连接,请检查网络");
    d.dispose();
    } else {
    mView.get().addSubscribe(d);
    }
    }

    @Override
    public void onComplete() {
    if (mView != null && mView.get() != null) {
    mView.get().hideProgress();
    }
    }

    @Override
    public void onError(Throwable e) {
    if (mView != null && mView.get() != null) {
    mView.get().hideProgress();
    }
    onNetError(e);
    }

    @Override
    public void onNext(T response) {
    if (response instanceof BaseResponse) {
    if (((BaseResponse) response).isOk(mView.get())) {
    onSuccess(response);
    } else {
    onServiceError(response);
    }
    } else {
    onOtherSuccess(response);
    }
    }

    /**
    * 请求成功并且服务器未下发异常
    *
    * @param response
    */
    protected abstract void onSuccess(T response);

    /**
    * 请求成功, 返回非继承自BaseResponse的非标准Bean或字符串
    *
    * @param response
    */
    protected void onOtherSuccess(T response) {

    }

    /**
    * 请求成功,服务器下发异常
    *
    * @param response
    */
    protected void onServiceError(T response) {

    }

    /**
    * 网络异常
    *
    * @param e
    */
    protected void onNetError(Throwable e) {
    if (mView != null && mView.get() != null) {
    NetworkError.error(mView.get(), e);
    }
    }
    }
  13. 放一张我画的流程图,比较魔性

三、使用示例

1
2
3
4
5
|-contract
|-presenter
|-model
|-bean
|-ui
  1. Contract

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public interface LoginContract {

    interface View extends BaseView {

    /**
    * 登陆成功
    * @param loginModel
    */
    void loginSuccess(LoginModel loginModel);

    }

    interface Presenter extends BasePresenter<View> {

    /**
    * 登陆
    */
    void login(String userName, String pwd);
    }
    }
  2. Activity

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    public class LoginActivity extends BaseMvpActivity<LoginPresenter> implements LoginContract.View {

    @BindView(R.id.et_login_user)
    ClearEditText mEtLoginUser;
    @BindView(R.id.et_login_password)
    ClearEditText mEtLoginPassword;

    @Override
    protected int getLayout() {
    return R.layout.activity_login;
    }

    @Override
    protected void initEventAndData() {
    initView();
    }

    @Override
    protected LoginPresenter createPresenter() {
    return new LoginPresenter();
    }

    private void initView() {
    // ...
    }

    private void toLogin() {
    mPresenter.login(mEtLoginUser.getText().toString(), mEtLoginPassword.getText().toString();
    }

    @Override
    public void loginSuccess(LoginModel loginModel) {
    startActivity(new Intent(mContext, MainActivity.class));
    finish();
    }

    @OnClick({R.id.tv_login_submit})
    public void onViewClicked(View view) {
    switch (view.getId()) {
    case R.id.tv_login_submit:
    //上传用户名和密码的方法
    toLogin();
    break;
    }
    }

    }
  3. Presenter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class LoginPresenter : SamplePresenter<LoginContract.View>(), LoginContract.Presenter {

    // 登录Model
    private val loginModel by lazy {
    LoginModel()
    }

    override fun login(userName: String, pwd: String) {
    mView.showProgress()
    loginModel
    .login()
    .subscribe(object : RetrofitSubscriber<BaseResponse<LoginModel>>(mView) {
    override fun onSuccess(response: BaseResponse<LoginModel>) {
    // 当前对象不为空时执行
    mView?.apply { loginSuccess(response) }
    }
    })
    }
    }
  4. Model

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class LoginModel {

    fun login(): Observable<LoginBean> {
    return RetrofitClient
    .getInstance()
    .gService
    .login()
    .compose(RxSchedulersUtils.rxObservableSchedulerHelper())
    }
    }

三、源码

  1. mvpretrofit是本文对应的代码

  2. retrofit是mvc模式下对应的代码

    https://github.com/sdwfqin/AndroidQuick/tree/2.x/app/src/main/java/com/sdwfqin/quickseed