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

  1. 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
  2. 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
  3. 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
  4. 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
  5. SamplePresenter

    SamplePresenter implements binding and unbinding views in BasePresenter

  6. 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
  7. 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
  8. 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
  9. 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
  10. RetrofitClient

    Retrofit, which uses singleton encapsulation, I won’t write it here, I’m sure you’ve all written it

  11. 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
  12. 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
  13. Here’s a flow chart I drew. It’s magic

Three, use examples

|-contract
|-presenter
|-model
    |-bean
|-ui
Copy the code
  1. 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
  2. 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
  3. 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
  4. Model

    class LoginModel {
    
    	fun login(a): Observable<LoginBean> {
    		return RetrofitClient
    				.getInstance()
    				.gService
    				.login()
    				.compose(RxSchedulersUtils.rxObservableSchedulerHelper())
    	}
    }
    Copy the code

Three, the source code

  1. Mvpretrofit is the corresponding code for this article

  2. Retrofit is the corresponding code in the MVC pattern

    Github.com/sdwfqin/And…