一、引入
9102年了,终于准备用mvp来重构一下了
之前写过Mvc模式下的Retrofit统一异常处理,这次用MVP重构过程中发现诸多不足之处,便重新进行修缮,使其在我看来更加优雅emmmmmm,文中不足之处,还望诸位同仁多多指点。
二、基本流程描述
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
42public interface BaseView {
/**
* 显示吐司
*
* @param msg 提示消息
*/
void showMsg(String msg);
/**
* 显示加载动画
*/
void showProgress();
/**
* 显示提示
*/
void showTip(int iconType, CharSequence tipWord) .Builder.IconType ;
/**
* 关闭加载动画
*/
void hideProgress();
/**
* 关闭提示
*/
void hideTip();
/**
* 跳转页面
*/
void startActivitySample(Class<?> cls);
/**
* Rx事件管理
*
* @param subscription
*/
void addSubscribe(Disposable subscription);
}BasePresenter
BasePresenter方法中只定义了绑定View与解绑View的接口
1
2
3
4
5
6public interface BasePresenter<T extends BaseView> {
void attachView(T view);
void detachView();
}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
protected void onCreate( 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();
}
protected void onDestroy() {
unSubscribe();
removePresenter();
AppManager.removeActivity(this);
super.onDestroy();
}
protected void initPresenter() {
}
protected void removePresenter() {
}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
27public abstract class BaseMvpActivity<T extends BasePresenter> extends BaseActivity {
protected T mPresenter;
protected void initPresenter() {
mPresenter = createPresenter();
if (mPresenter != null) {
mPresenter.attachView(this);
}
}
protected void removePresenter() {
if (mPresenter != null) {
mPresenter.detachView();
}
}
/**
* 创建Presenter
*
* @return
*/
protected abstract T createPresenter();
}SamplePresenter
SamplePresenter实现了BasePresenter中的绑定view与解绑view
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
20public 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...
}NetworkError
根据不同的标志统一处理异常以及服务器返回的错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public 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;
}
}
}ServerException
自定义服务器异常
1
2
3
4
5
6
7
8
9public class ServerException extends RuntimeException {
public int code;
public ServerException(int code, String message) {
super(message);
this.code = code;
}
}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
96public 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;
}
}
}RetrofitClient
使用单例封装的Retrofit,这里就不写了,相信大家都写过
ServiceApi
这个是Api接口与
10
对应,这里用的Observable
,感觉没必要用Flowable
,用Flowable
的话下面12
这个就不是这么写了1
2
3
4
5
6
7
8public interface ServiceApi {
/**
* 测试接口
*/
Observable<BaseResponse<LoginModel>> login();
}RetrofitSubscriber(Observer)
通过
BaseView
调用在BaseActivity/Fragment中实现的addSubscribe
将Disposable
添加到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
83public abstract class RetrofitSubscriber<T> implements Observer<T> {
private final WeakReference<BaseView> mView;
public RetrofitSubscriber(BaseView view) {
super();
mView = new WeakReference<>(view);
}
public void onSubscribe(Disposable d) {
if (!NetworkUtils.isConnected()) {
mView.get().showMsg("网络未连接,请检查网络");
d.dispose();
} else {
mView.get().addSubscribe(d);
}
}
public void onComplete() {
if (mView != null && mView.get() != null) {
mView.get().hideProgress();
}
}
public void onError(Throwable e) {
if (mView != null && mView.get() != null) {
mView.get().hideProgress();
}
onNetError(e);
}
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);
}
}
}放一张我画的流程图,比较魔性
三、使用示例
1 | |-contract |
Contract
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public interface LoginContract {
interface View extends BaseView {
/**
* 登陆成功
* @param loginModel
*/
void loginSuccess(LoginModel loginModel);
}
interface Presenter extends BasePresenter<View> {
/**
* 登陆
*/
void login(String userName, String pwd);
}
}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
47public class LoginActivity extends BaseMvpActivity<LoginPresenter> implements LoginContract.View {
ClearEditText mEtLoginUser;
ClearEditText mEtLoginPassword;
protected int getLayout() {
return R.layout.activity_login;
}
protected void initEventAndData() {
initView();
}
protected LoginPresenter createPresenter() {
return new LoginPresenter();
}
private void initView() {
// ...
}
private void toLogin() {
mPresenter.login(mEtLoginUser.getText().toString(), mEtLoginPassword.getText().toString();
}
public void loginSuccess(LoginModel loginModel) {
startActivity(new Intent(mContext, MainActivity.class));
finish();
}
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.tv_login_submit:
//上传用户名和密码的方法
toLogin();
break;
}
}
}Presenter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class 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) }
}
})
}
}Model
1
2
3
4
5
6
7
8
9
10class LoginModel {
fun login(): Observable<LoginBean> {
return RetrofitClient
.getInstance()
.gService
.login()
.compose(RxSchedulersUtils.rxObservableSchedulerHelper())
}
}
三、源码
mvpretrofit是本文对应的代码
retrofit是mvc模式下对应的代码
https://github.com/sdwfqin/AndroidQuick/tree/2.x/app/src/main/java/com/sdwfqin/quickseed