9102年了,終於準備用mvp來重構一下了java
以前寫過Mvc模式下的Retrofit統一異常處理,此次用MVP重構過程當中發現諸多不足之處,便從新進行修繕,使其在我看來更加優雅emmmmmm,文中不足之處,還望諸位同仁多多指點。git
BaseViewgithub
BaseView接口定義了可能用到的方法,特別是addSubscribe
,用來管理RxJava生命週期。bash
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);
}
複製代碼
BasePresenter服務器
BasePresenter方法中只定義了綁定View與解綁View的接口網絡
public interface BasePresenter<T extends BaseView> {
void attachView(T view);
void detachView();
}
複製代碼
BaseActivity/Fragmentmvc
這個類是封裝了一些經常使用方法,而且實現了BaseView的所有接口。 而且預留了兩個用於mvp模式的空方法app
@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() {
}
複製代碼
BaseMvpActivity/Fragmentide
實現了BaseActivity/Fragment中的預留方法測試
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();
}
複製代碼
SamplePresenter
SamplePresenter實現了BasePresenter中的綁定view與解綁view
BaseResponse
RESTful API的基類,看下代碼很容易明白,可是須要注意下isOk(BaseView view)
方法
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...
}
複製代碼
NetworkError
根據不一樣的標誌統一處理異常以及服務器返回的錯誤
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;
}
}
}
複製代碼
ServerException
自定義服務器異常
public class ServerException extends RuntimeException {
public int code;
public ServerException(int code, String message) {
super(message);
this.code = code;
}
}
複製代碼
RetrofitException
自定義網絡異常,獲取錯誤緣由
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;
}
}
}
複製代碼
RetrofitClient
使用單例封裝的Retrofit,這裏就不寫了,相信你們都寫過
ServiceApi
這個是Api接口與10
對應,這裏用的Observable
,感受不必用Flowable
,用Flowable
的話下面12
這個就不是這麼寫了
public interface ServiceApi {
/** * 測試接口 */
@POST("test")
Observable<BaseResponse<LoginModel>> login();
}
複製代碼
RetrofitSubscriber(Observer)
經過BaseView
調用在BaseActivity/Fragment中實現的addSubscribe
將Disposable
添加到CompositeDisposable
中,在頁面銷燬時先中斷請求,以避免形成view銷燬了還去調用致使空指針異常。而且根據Observer
的接口經過BaseView
來處理加載動畫(在BaseActivity/Fragment中實現)。
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().showProgress();
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);
}
}
}
複製代碼
放一張我畫的流程圖,比較魔性
|-contract
|-presenter
|-model
|-bean
|-ui
複製代碼
Contract
public interface LoginContract {
interface View extends BaseView {
/** * 登錄成功 * @param loginModel */
void loginSuccess(LoginModel loginModel);
}
interface Presenter extends BasePresenter<View> {
/** * 登錄 */
void login(String userName, String pwd);
}
}
複製代碼
Activity
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;
}
}
}
複製代碼
Presenter
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) }
}
})
}
}
複製代碼
Model
class LoginModel {
fun login(): Observable<LoginBean> {
return RetrofitClient
.getInstance()
.gService
.login()
.compose(RxSchedulersUtils.rxObservableSchedulerHelper())
}
}
複製代碼
mvpretrofit是本文對應的代碼
retrofit是mvc模式下對應的代碼