A, introducing
9102 years, finally ready for a refactoring with MVP
I have written about the unified exception handling of Retrofit in Mvc mode. I found a lot of shortcomings in the MVP reconstruction process, so I revised it to make it more elegant in my opinion.
Ii. Description of basic process
-
BaseView
The BaseView interface defines the methods that may be used, in particular addSubscribe, to manage the RxJava lifecycle.
public interface BaseView { /** * display toast **@paramMSG Prompt message */ void showMsg(String msg); /** * display loading animation */ void showProgress(a); /** * Display prompt */ void showTip(@QMUITipDialog.Builder.IconType int iconType, CharSequence tipWord); /** * Close loading animation */ void hideProgress(a); /** * Close prompt */ void hideTip(a); /** * jump to the page */ void startActivitySample(Class cls); /** * Rx event Management **@param subscription */ void addSubscribe(Disposable subscription); } Copy the code
-
BasePresenter
The BasePresenter method only defines interfaces to bind and unbind views
public interface BasePresenter<T extends BaseView> { void attachView(T view); void detachView(a); } Copy the code
-
BaseActivity/Fragment
This class encapsulates some common methods and implements all BaseView interfaces. Two empty methods are reserved for MVP mode
@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); // Call the binding Presenter method here initPresenter(); initEventAndData(); } @Override protected void onDestroy(a) { unSubscribe(); removePresenter(); AppManager.removeActivity(this); super.onDestroy(); } protected void initPresenter(a) {}protected void removePresenter(a) {}Copy the code
-
BaseMvpActivity/Fragment
Realize the reservation method in BaseActivity/Fragment
public abstract class BaseMvpActivity<T extends BasePresenter> extends BaseActivity { protected T mPresenter; @Override protected void initPresenter(a) { mPresenter = createPresenter(); if(mPresenter ! =null) { mPresenter.attachView(this); }}@Override protected void removePresenter(a) { if(mPresenter ! =null) { mPresenter.detachView(); }}/** * Create Presenter@return* / protected abstract T createPresenter(a); } Copy the code
-
SamplePresenter
SamplePresenter implements binding and unbinding views in BasePresenter
-
BaseResponse
RESTful API base class, look at the code is easy to understand, but need to pay attention to the isOk(BaseView View) method
public class BaseResponse<T> { private int statusCode; private String message; private T data; public boolean isOk(BaseView view) { // statusCode == 1 The server request succeeded if (statusCode == 1) { return true; } else { // The server returned an error with a normal request NetworkError.error(view, new ServerException(statusCode, message)); return false; }}// get/set... } Copy the code
-
NetworkError
Handle exceptions and errors returned by the server uniformly according to different flags
public class NetworkError { public static void error(BaseView view, Throwable throwable) { RetrofitException.ResponeThrowable responeThrowable = RetrofitException.retrofitException(throwable); // The error code can be determined by the error code to implement the corresponding response 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: // Jump to the login page view.startActivitySample(LoginActivity.class); break; default: view.showMsg(responeThrowable.message); break; }}}Copy the code
-
ServerException
Custom server exception
public class ServerException extends RuntimeException { public int code; public ServerException(int code, String message) { super(message); this.code = code; }}Copy the code
-
RetrofitException
Obtain the cause of the user-defined network exception
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 = "Network error"; break; } return ex; } else if (e instanceof ServerException) { // The server delivered an error 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 = "Parsing error"; return ex; } else if (e instanceof ConnectException || e instanceof SocketTimeoutException || e instanceof UnknownHostException) { ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR); ex.message = "Connection failed"; return ex; } else if (e instanceof SSLHandshakeException) { ex = new ResponeThrowable(e, ERROR.SSL_ERROR); ex.message = "Certificate verification failed"; return ex; } else { ex = new ResponeThrowable(e, ERROR.UNKNOWN); ex.message = "Unknown error"; returnex; }}/** * the convention is abnormal */ class ERROR { /** * Unknown error */ public static final int UNKNOWN = 1000; /** * parsing error */ public static final int PARSE_ERROR = 1001; /** * Network error */ public static final int NETWORD_ERROR = 1002; /** * protocol error */ public static final int HTTP_ERROR = 1003; /** * Certificate error */ 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; }}}Copy the code
-
RetrofitClient
Retrofit, which uses singleton encapsulation, I won’t write it here, I’m sure you’ve all written it
-
ServiceApi
This is an Api that corresponds to 10, an Observable that feels like it doesn’t have to use Flowable. It doesn’t have to use Flowable
public interface ServiceApi { /** * test interface */ @POST("test") Observable<BaseResponse<LoginModel>> login(); } Copy the code
-
RetrofitSubscriber (Observer)
AddSubscribe in BaseActivity/Fragment adds Disposable to the CompositeDisposable with a BaseView call, which interrupts the request at the time of page destruction to avoid null pointer exceptions when the View is destroyed. Loading animations are handled through the BaseView (implemented in BaseActivity/Fragment) according to the Observer interface.
public abstract class RetrofitSubscriber<T> implements Observer<T> { private final WeakReference<BaseView> mView; public RetrofitSubscriber(BaseView view) { super(a); mView =new WeakReference<>(view); } @Override public void onSubscribe(Disposable d) { if(! NetworkUtils.isConnected()) { mView.get().showMsg("Network is not connected. Please check the network."); d.dispose(); } else{ mView.get().showProgress(); mView.get().addSubscribe(d); }}@Override public void onComplete(a) { 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); }}/** * The request was successful and the server did not send an exception **@param response */ protected abstract void onSuccess(T response); /** * Request successful, returns a nonstandard Bean or string that does not inherit from BaseResponse **@param response */ protected void onOtherSuccess(T response) {}/** * The server failed to send an exception **@param response */ protected void onServiceError(T response) {}/** * Network exception **@param e */ protected void onNetError(Throwable e) { if(mView ! =null&& mView.get() ! =null) { NetworkError.error(mView.get(), e); }}}Copy the code
-
Here’s a flow chart I drew. It’s magic
Three, use examples
|-contract
|-presenter
|-model
|-bean
|-ui
Copy the code
-
Contract
public interface LoginContract { interface View extends BaseView { /** * Login successful *@param loginModel */ void loginSuccess(LoginModel loginModel); } interface Presenter extends BasePresenter<View> { /** * 登陆 */ void login(String userName, String pwd); }}Copy the code
-
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(a) { return R.layout.activity_login; } @Override protected void initEventAndData(a) { initView(); } @Override protected LoginPresenter createPresenter(a) { return new LoginPresenter(); } private void initView(a) { // ... } private void toLogin(a) { 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: // How to upload the username and password toLogin(); break; }}}Copy the code
-
Presenter
class LoginPresenter : SamplePresenter<LoginContract.View>(), Logincontract.presenter {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>) {// Execute mView when the current object is not empty? .apply { loginSuccess(response) } } }) } }Copy the code
-
Model
class LoginModel { fun login(a): Observable<LoginBean> { return RetrofitClient .getInstance() .gService .login() .compose(RxSchedulersUtils.rxObservableSchedulerHelper()) } } Copy the code
Three, the source code
-
Mvpretrofit is the corresponding code for this article
-
Retrofit is the corresponding code in the MVC pattern
Github.com/sdwfqin/And…