The combination of Android MVP development mode and Rxjava+Retrofit (advanced version)

1. Basic introduction

1. What is mvp

MVP is one of the android development architectures, and each letter of MVP represents Model, View and Presenter respectively.

①model is responsible for processing network data
②presenter is the bridge between model and view, responsible for communication with both ends
③view is the view layer

2. Why is there MVP

The birth of mvp benefited from mvc. Mvc did contribute a lot and has gone through ups and downs, but the coupling of mvc is too serious, so mvp was born. In the mvp mode, the model and view do not interact directly, but use the presenter as a bridge. In this way, the division of labor at each layer is more clear
. Just go to p. As for whether p is directly given to v or processed and then given to v, how to give it is all a matter of p.
②As for p, regardless of the post or get used by the model, p only needs to initiate a request to m, and after getting the result, call back the result to v, regardless of whether v wants to pop up a dialog Box or toast or jump page.
③v only needs to send the user's action or input content to p, and wait for p to respond!

3. This mvp is different from others

This mvp is mainly reflected in the fact that p is different. After the p layer gets the instance of v, it obtains the view instance through the dynamic proxy, and the execution of the view method is handed over to the InvocationHandler for processing, which can effectively avoid when the view does not exist. , also execute the view method. It is recommended to pass in the activity instance in P. In fact, what is needed is the name of the applicat and activity. I haven't thought of a better way yet, so I will use it first. If the parameter passed in is not activity or no parameter is passed, remember to call the detachView method of p at the place where the page exits and should be recycled.

2. Implementation steps

1. On what basis?

First of all, there must be a major premise , that is, the data format returned by all interfaces is the same. For example, the data format returned by my interface is like this

{
    "code": 200,
    "message": "提交成功"
}

or something like this

{
    "code": 200,
    "message": "",
    "data": {
        "userName": "张三",
        "headImage": "http://192.168.3.11/file/user/hf6d4g88a.jpg",
        "sex": 1,
        "bir": "2020-01-01"
    }
}

They all have something in common, such as code and message, so data is generic! So the class received by the defined response body is

public class CallResult<T> {
    public int code;
    public String message;
    public T data;
}

2. Package Retrofit

Considering that sometimes it is necessary to add various data in the header of the request, such as appVersion, etc., and the timeout time of uploading files is generally different from that of ordinary interfaces, so there is this kind of encapsulation

2.1 start packaging

import android.text.TextUtils;
import android.util.Log;

import com.example.mvp.OnHttpResultListener;
import com.example.mvp.retrofit2.convert.MyConverterFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.Interceptor;
import okhttp3.OkHttpClient.Builder;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

/**
 * @author Administrator
 */
public class Retrofit2Manager {
    /**
     * 默认的请求时间
     */
    private long timeOut = 20000L;
    /**
     * 监听请求过程
     */
    private OnHttpResultListener onHttpResultListener;
    /**
     * 服务器地址
     */
    private final String baseUrl;
    /**
     * 请求头
     */
    private final Map<String, String> map = new HashMap<>();
    /**
     * 自定义拦截器
     */
    private final List<Class<? extends Interceptor>> interceptors = new ArrayList<>();

    /**
     * 静态方法,入口
     *
     * @param baseUrl 路径
     * @return this
     */
    public static Retrofit2Manager with(String baseUrl) {
        return new Retrofit2Manager(baseUrl);
    }

    /**
     * 私有构造方法
     *
     * @param baseUrl 服务器路径
     */
    private Retrofit2Manager(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    /**
     * 超时时间
     *
     * @param timeOut timeOut
     * @return this
     */
    public Retrofit2Manager setTimeOut(long timeOut) {
        this.timeOut = timeOut;
        return this;
    }

    /**
     * 监听请求过程
     *
     * @param onHttpResultListener onHttpResultListener
     * @return this
     */
    public Retrofit2Manager setOnHttpResultListener(OnHttpResultListener onHttpResultListener) {
        this.onHttpResultListener = onHttpResultListener;
        return this;
    }

    /**
     * 添加自定义请求头
     *
     * @param key   key
     * @param value value
     * @return this
     */
    public Retrofit2Manager addHeadres(String key, String value) {
        if (TextUtils.isEmpty(key)) {
            return this;
        }
        if (TextUtils.isEmpty(value)) {
            value = "";
        }
        map.put(key, value);
        return this;
    }

    public Retrofit2Manager add(Class<? extends Interceptor> mClass) {
        interceptors.add(mClass);
        return this;


    }

    /**
     * 返回retrofit2的实例
     *
     * @return retrofit2
     */
    public Retrofit retrofit() {
        Builder okBuilder = new Builder();
        okBuilder.readTimeout(this.timeOut, TimeUnit.MILLISECONDS);
        okBuilder.writeTimeout(this.timeOut, TimeUnit.MILLISECONDS);
        okBuilder.connectTimeout(this.timeOut, TimeUnit.MILLISECONDS);
        okBuilder.addInterceptor(new LogInterceptor(map, onHttpResultListener));
        try {
            for (Class<? extends Interceptor> mClass : interceptors) {
                okBuilder.addInterceptor(mClass.newInstance());
            }
        } catch (Exception e) {
            Log.e("mvp[error]", e.getMessage());
            e.printStackTrace();
        }
        return (new Retrofit.Builder()).client(okBuilder.build())
                .baseUrl(this.baseUrl)
                //自定义解析
                .addConverterFactory(MyConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }
}

2.2 Why do you need to customize the analysis?

When the data returned by the server is

{
    "code": 200,
    "message": "",
    "data": [
        {
            "userName": "李四",
            "headImage": "http://192.168.3.11/file/user/hf6d4g3243288a.jpg",
            "sex": 1,
            "bir": "2020-01-01"
        },
        {
            "userName": "张三",
            "headImage": "http://192.168.3.11/file/user/hf6d4g84348a.jpg",
            "sex": 2,
            "bir": "2020-01-17"
        },
        {
            "userName": "王五",
            "headImage": "http://192.168.3.11/file/user/hf6d4345436g88a.jpg",
            "sex": 1,
            "bir": "2020-03-01"
        }
    ]
}

When , the entity class we defined can receive data normally. If the data returned by the interface is

{
    "code": 401,
    "message": "登录状态已失效",
    "data": null
}

, you will find that the direct json conversion crashes, because null cannot be converted into a list, so we have to define the parsing factory ourselves, the following is part of the code

    @Override
    public T convert(@NonNull ResponseBody value) {
        String str = "";
        Object var3;
        try {
            if (value.contentLength() != 0L) {
                str = value.source().readUtf8();
                var3 = this.convert(str, this.type);
                return (T) var3;
            }
            str = "{\"code\":90000,\"message\":\"服务器无响应\"}";
            var3 = this.convert(str, CallResult.class);
        } catch (Exception var8) {
            //当转换出现异常,就用Void进行转换
            Object var4 = this.convert(str, CallResult.class);
            return (T) var4;
        } finally {
            value.close();
        }
        return (T) var3;
    }


3. Create various Base base classes for mvp

3.1 model layer BaseModel

OnHttpResultListener is defined by itself and is used to receive interface parameters. It is very useful for debugging, and the online version can be ignored

import android.util.Log;

import com.example.mvp.OnHttpResultListener;
import com.example.mvp.retrofit2.Retrofit2Manager;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;

public class BaseModel implements OnHttpResultListener {

    protected <T> T createService(String ip, Class<T> mClass) {
        return Retrofit2Manager.with(ip).setOnHttpResultListener(this).retrofit().create(mClass);
    }

    @Override
    public void onResponse(String method, String requestUrl, String requestHeaders, String requestParams, int responseCode, String responseData) {
        String sb = "\n【请求方法】:" + method +
                "\n【请求路径】:" + requestUrl +
                "\n【请求头】:" + requestHeaders +
                "\n【请求参数】:" + requestParams +
                "\n【返回参数】:" + responseData;
        Log.d("exccd(mvp-http)", sb);
    }

    /**
     * 发起请求,并且在ui线程执行回调
     *
     * @param observable observable
     * @param <T>        泛型
     */
    protected <T> Observable<T> callBackOnUi(Observable<T> observable) {
        return observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

    /**
     * 发起请求,并且在新的子线程执行回调
     *
     * @param observable observable
     * @param <T>        泛型
     */
    protected <T> Observable<T> callBackOnThread(Observable<T> observable) {
        return observable.subscribeOn(Schedulers.io())
                .observeOn(Schedulers.newThread());
    }
}

3.2 View layer IBaseView

In order to make it easier for the ui to implement this callback, I return the message returned by the server and all other possible prompts through the s field, so whether it is "login status has expired" or "network connection exception", it is all in s callback. According to the project, it can be split if necessary.

/**
 * ui回调
 *
 * @param <T> 泛型
 */
public interface IBaseView<T> {
    /**
     * ui回调
     *
     * @param b    是否请求成功
     * @param i    类型
     * @param s    描述
     * @param data 泛型
     */
    void onCallBack(boolean b, int i, String s, T data);
}

3.3 Presenter layer

The key to this layer is IBasePresenter and its implementation class BasePresenter

3.3.1 IBasePresenter

The start method is the entry point for initiating a request. When using it, you need to pass in the method of the model, and then bind it to the view.

/**
 * presenter需要具备的基础方法
 */
public interface IBasePresenter {
    /**
     * 开始发起请求
     *
     * @param observable model层返回的obs
     * @param iBaseView  视图,回调
     * @param <T>        泛型
     */
    <T> void start(Observable<CallResult<T>> observable, IBaseView<T> iBaseView);

    /**
     * 成功回调
     *
     * @param iBaseView 视图、回调
     * @param data      数据
     * @param <T>       泛型
     */
    <T> void viewCallBackSuccess(IBaseView<T> iBaseView, CallResult<T> data);

    /**
     * 错误回调
     *
     * @param iBaseView 视图、回调
     * @param e         错误信息
     * @param <T>       泛型
     */
    <T> void viewCallBackError(IBaseView<T> iBaseView, Throwable e);

    /**
     * 解绑
     */
    void detachView();
}

3.3.2 BasePresenter

BasePresenter handles some callbacks, converts the results of some abnormal interface requests into Chinese (specified descriptions) and then calls back to the view. All the data here can be defined by yourself. In addition, if you need to pop up a window to log out under certain circumstances, it is recommended You can create a new MyBasePresenter extend BasePresenter, and then rewrite onTokenErrorCallBack(), but the judgment logic needs to be changed.

public abstract class BasePresenter<M> implements IBasePresenter {
    /**
     * 未授权登录,登录状态已失效
     */
    public static final int UNAUTHORIZED = 401;
    /**
     * 请求成功
     */
    public static final int SUCCESS = 200;
    /**
     * 请求被禁止
     */
    public static final int FORBIDDEN = 403;
    /**
     * 接口失效
     */
    public static final int NOT_FOUND = 404;
    /**
     * 请求超时
     */
    public static final int REQUEST_TIMEOUT = 408;
    /**
     * 服务器错误
     */
    public static final int INTERNAL_SERVER_ERROR = 500;
    /**
     * 错误的网关
     */
    public static final int BAD_GATEWAY = 502;
    /**
     * 服务器不可用
     */
    public static final int SERVICE_UNAVAILABLE = 503;
    /**
     * 网络超时
     */
    public static final int GATEWAY_TIMEOUT = 504;
    /**
     * 在默认线程回调
     */
    private boolean callBackInLoop = false;
    /**
     * 是否已经解绑了,避免重复解绑
     */
    private boolean isDttached = false;
    /**
     * model层
     */
    protected M module;
    /**
     * 视图
     */
    private final Map<Integer, IBaseView<?>> mapView = new HashMap<>();
    /**
     * 视图引用
     */
    private final Map<Integer, WeakReference<?>> mapReference = new HashMap<>();
    /**
     * 请求对象
     */
    private final Map<Integer, Disposable> mapDisposables = new HashMap<>();
    /**
     * 主线程
     */
    protected Handler handler;

    /**
     * 构造方法
     * 您需要手动{@link #detachView()}解绑
     */
    public BasePresenter() {
        onCreate(null);
    }

    /**
     * 构造方法
     *
     * @param activity activity的实例
     */
    public BasePresenter(Activity activity) {
        onCreate(activity);
    }

    /**
     * 构造方法
     *
     * @param context 如果这是个activity的实例,那么不需要手动{@link #detachView()}即可解绑,否则您需要调用他
     */
    public BasePresenter(Context context) {
        if (context instanceof Activity) {
            onCreate((Activity) context);
        } else {
            onCreate(null);
        }
    }

    /**
     * 初始化方法
     */
    private void onCreate(Activity activity) {
        this.handler = new Handler(Looper.getMainLooper());
        if (this.module == null) {
            this.module = this.createModel();
        }
        if (activity != null) {
            String acName = activity.getLocalClassName();
            Application app = activity.getApplication();
            Application.ActivityLifecycleCallbacks callbacks = new Application.ActivityLifecycleCallbacks() {
                @Override
                public void onActivityCreated(Activity activity, Bundle bundle) {

                }

                @Override
                public void onActivityStarted(Activity activity) {

                }

                @Override
                public void onActivityResumed(Activity activity) {

                }

                @Override
                public void onActivityPaused(Activity activity) {

                }

                @Override
                public void onActivityStopped(Activity activity) {

                }

                @Override
                public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

                }

                @Override
                public void onActivityDestroyed(Activity activity) {
                    if (acName.equals(activity.getLocalClassName())) {
                        detachView();
                        app.unregisterActivityLifecycleCallbacks(this);
                    }
                }
            };
            app.registerActivityLifecycleCallbacks(callbacks);
        }
    }

    /**
     * 绑定
     *
     * @param view 视图
     */
    @SuppressWarnings("all")
    private <T, V extends IBaseView<T>> void attachView(V view) {
        if (view != null) {
            WeakReference<V> weakReference = new WeakReference<V>(view);
            mapReference.put(view.hashCode(), weakReference);
            ClassLoader classLoader = view.getClass().getClassLoader();
            Class<?>[] interfaces = view.getClass().getInterfaces();
            InvocationHandler invocationHandler = new MvpViewHandler((IBaseView) weakReference.get());
            IBaseView<T> v = (V) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
            mapView.put(view.hashCode(), v);
        }
    }

    /**
     * 是否在默认线程回调
     *
     * @param callBackInLoop 如果是false,回调会在ui线程处理,否则就是在发起默认的线程回调
     */
    public void setCallBackInLoop(boolean callBackInLoop) {
        this.callBackInLoop = callBackInLoop;
    }

    /**
     * 页面是否已经不存在了
     *
     * @param iBaseView 视图
     * @param <T>       泛型
     * @return true存在,则回调,否则忽略
     */
    protected <T> boolean isViewAttached(IBaseView<T> iBaseView) {
        if (iBaseView == null) {
            return false;
        }
        int key = iBaseView.hashCode();
        IBaseView<?> view = mapView.get(key);
        WeakReference<?> weakReference = mapReference.get(key);
        return view != null && weakReference != null && weakReference.get() != null;
    }

    /**
     * 创建module
     *
     * @return M
     */
    protected abstract M createModel();

    /**
     * 请求是否成功
     *
     * @param data 响应体
     * @return 成功true,失败false
     */
    protected <T> boolean isSuccess(CallResult<T> data) {
        return data != null && data.code == SUCCESS;
    }

    /**
     * 开始发起请求
     *
     * @param observable model层返回的obs
     * @param baseView   视图、回调
     * @param <T>        泛型
     */
    @Override
    public <T> void start(Observable<CallResult<T>> observable, IBaseView<T> baseView) {
        attachView(baseView);
        mapDisposables.put(baseView.hashCode(), observable
                .subscribe(data -> viewCallBackSuccess(baseView, data), e -> viewCallBackError(baseView, e)));
    }

    /**
     * 成功回调
     *
     * @param view 视图、回调
     * @param data 数据
     * @param <T>  泛型
     */
    @Override
    public <T> void viewCallBackSuccess(IBaseView<T> view, CallResult<T> data) {
        if (callBackInLoop) {
            _viewCallBackSuccess(view, data);
        } else {
            if (Looper.myLooper() == Looper.getMainLooper()) {
                _viewCallBackSuccess(view, data);
            } else {
                handler.post(() -> _viewCallBackSuccess(view, data));
            }
        }
    }

    /**
     * 错误回调
     *
     * @param view 视图、回调
     * @param e    错误信息
     * @param <T>  泛型
     */
    @Override
    public <T> void viewCallBackError(IBaseView<T> view, Throwable e) {
        if (callBackInLoop) {
            _viewCallBackError(view, e);
        } else {
            if (Looper.myLooper() == Looper.getMainLooper()) {
                _viewCallBackError(view, e);
            } else {
                handler.post(() -> _viewCallBackError(view, e));
            }
        }
    }

    /**
     * 解绑
     */
    @Override
    public void detachView() {
        if (isDttached) {
            return;
        }
        isDttached = true;
//        this.module = null;
        this.handler.removeCallbacksAndMessages(null);
        for (WeakReference<?> weakReference : mapReference.values()) {
            if (weakReference != null) {
                weakReference.clear();
            }
        }
        mapReference.clear();
        mapView.clear();
        try {
            for (Disposable disposable : mapDisposables.values()) {
                if (disposable != null && !disposable.isDisposed()) {
                    disposable.dispose();
                }
            }
        } catch (Exception e) {
            Log.e("mvp[error]", e.getMessage());
        }
    }

    /**
     * 统一执行成功回调,看{@link #viewCallBackSuccess}
     */
    private <T> void _viewCallBackSuccess(IBaseView<T> view, CallResult<T> data) {
        if (data.code == UNAUTHORIZED) {
            onTokenErrorCallBack(data.message);
        }
        if (isViewAttached(view)) {
            view.onCallBack(data.code == SUCCESS, data.code, data.message, data.data);
        }
    }

    /**
     * 统一执行错误回调,看{@link #viewCallBackError}
     */
    private <T> void _viewCallBackError(IBaseView<T> view, Throwable e) {
        if (isViewAttached(view)) {
            try {
                if (e instanceof HttpException) {
                    HttpException httpException = (HttpException) e;
                    switch (httpException.code()) {
                        case UNAUTHORIZED:
                            callBackError(view, "登录验证已过期");
                            onTokenErrorCallBack("登录验证已过期");
                            break;
                        case INTERNAL_SERVER_ERROR:
                            callBackError(view, "服务器错误");
                            break;
                        case FORBIDDEN:
                        case NOT_FOUND:
                            callBackError(view, "无效的请求");
                            break;
                        case REQUEST_TIMEOUT:
                        case GATEWAY_TIMEOUT:
                        case BAD_GATEWAY:
                        case SERVICE_UNAVAILABLE:
                        default:
                            callBackError(view, httpException.getMessage());
                            break;
                    }
                } else if (e instanceof ConnectException) {
                    callBackError(view, "网络连接异常,请检查您的网络状态");
                } else if (e instanceof SocketTimeoutException) {
                    callBackError(view, "网络连接超时,请检查您的网络状态,稍后重试");
                } else if (e instanceof UnknownHostException) {
                    callBackError(view, "网络异常,请检查您的网络状态");
                } else if (e instanceof JSONException
                        || e instanceof ParseException) {
                    callBackError(view, "数据解析错误");
                } else if (e instanceof SSLHandshakeException) {
                    callBackError(view, "证书验证失败");
                } else if (e instanceof RuntimeException) {
                    callBackError(view, "运行时异常");
                } else {
                    callBackError(view, e.toString());
                }
            } catch (Exception e1) {
                Log.e("mvp[error]", e.getMessage());
            }
        }
    }

    /**
     * {@link #_viewCallBackError}
     */
    private <T> void callBackError(IBaseView<T> view, String message) {
        view.onCallBack(false, 9000, message, null);
        Log.e("excce", "UI回调错误信息:" + message);
    }

    /**
     * 返回一个value类型为Object的哈希表
     *
     * @param initSize 大小
     * @return Map
     */
    protected Map<String, Object> createMap(int initSize) {
        return new HashMap<>(initSize);
    }

    /**
     * 返回一个value类型为Integer的哈希表
     *
     * @param initSize 大小
     * @return Map
     */
    protected Map<String, Integer> createMapInt(int initSize) {
        return new HashMap<>(initSize);
    }

    /**
     * 返回一个value类型为String的哈希表
     *
     * @param initSize 大小
     * @return Map
     */
    protected Map<String, String> createMapStr(int initSize) {
        return new HashMap<>(initSize);
    }

    /**
     * 登录状态失效,需要回到登录页
     *
     * @param message message
     */
    protected void onTokenErrorCallBack(String message) {

    }

    /**
     * 动态代理
     *
     * @param <T> 泛型
     */
    private class MvpViewHandler<T> implements InvocationHandler {
        private final IBaseView<T> mvpView;

        MvpViewHandler(IBaseView<T> mvpView) {
            this.mvpView = mvpView;
        }

        @Override
        @SuppressWarnings("SuspiciousInvocationHandlerImplementation")
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (isViewAttached(mvpView)) {
                return method.invoke(this.mvpView, args);
            } else {
                Log.d("excci", "页面已关闭,不执行view层方法!");
                return null;
            }
        }
    }
}

4. Start using

After a series of long and complicated packages, it can finally be used. Here, the login interface and the user information interface are taken as examples to show how to use two different modules.

4.1 Write the IModelCom of the public module according to the interface document

This is actually the familiar Service

/**
 * 公共方法模块
 */
public interface IModelCom {
    /**
     * 登录
     *
     * @param map phone,passWord
     */
    @POST("api/user/login")
    Observable<CallResult<LoginResponseBean>> login(@Body Map<String, Object> map);

    /**
     * 注册
     *
     * @param map phone,code,passWord
     */
    @POST("api/user/register")
    Observable<CallResult<Void>> register(@Body Map<String, Object> map);

    /**
     * 忘记密码
     *
     * @param map phone,code,newPassWord
     */
    @POST("api/user/forget")
    Observable<CallResult<Void>> forgetPwd(@Body Map<String, Object> map);
}

The other IModelUserCenter is written in the same way, ignore it.

4.2 Look at the implementation class

Whether to call back in the main thread is up to you

public class ModelCom extends BaseModel implements IModelCom {

    private static final class IHolder {
        static final ModelCom i = new ModelCom();
    }

    public static ModelCom getInstance() {
        return IHolder.i;
    }

    private final IModelCom api;

    private ModelCom() {
        api = createService(UrlUtils.IP, IModelCom.class);
    }

    @Override
    public Observable<CallResult<LoginResponseBean>> login(Map<String, Object> map) {
        return callBackOnUi(api.login(map));
    }

    @Override
    public Observable<CallResult<Void>> register(Map<String, Object> map) {
        return callBackOnUi(api.register(map));
    }

    @Override
    public Observable<CallResult<Void>> forgetPwd(Map<String, Object> map) {
        return callBackOnUi(api.forgetPwd(map));
    }
}

4.3 Contract class

From the current point of view, the contract class only defines the P layer, because the m layer is already retrofit, and the v layer only has callback parameters.

/**
 * 公共模块契约类
 */
public interface IContractCom {
    /**
     * 公共p层
     * {@link com.example.mvpdemo.mvp.presenter.PresenterCom}
     */
    interface IPresenterCom extends IBasePresenter {

        /**
         * 登录
         *
         * @param phone    手机号
         * @param passWord 密码
         */
        void login(String phone, String passWord, IBaseView<LoginResponseBean> view);

        /**
         * 注册
         *
         * @param phone    手机号
         * @param passWord 密码
         * @param code     验证码
         * @param view     回调
         */
        void register(String phone, String passWord, String code, IBaseView<Void> view);

        /**
         * 忘记密码
         *
         * @param phone       手机号
         * @param code        验证码
         * @param newPassWord 新密码
         * @param view        回调
         */
        void forGetPassWord(String phone, String code, String newPassWord, IBaseView<Void> view);
    }
}

4.4 Realization of P

/**
 * 公共P
 */
public class PresenterCom extends BasePresenter<IModelCom> implements IContractCom.IPresenterCom {
    public PresenterCom(Activity activity) {
        super(activity);
    }

    @Override
    public void login(String phone, String passWord, IBaseView<LoginResponseBean> view) {
        Map<String, Object> map = createMap(2);
        map.put("phone", phone);
        map.put("passWord", passWord);
        start(module.login(map).map(resp -> {
            if (isSuccess(resp)) {
                //如果登录成功了,则保存token,用户名等信息
                Log.i("loginResult", resp.data.token);
                Log.i("loginResult", resp.data.userName);
                Log.i("loginResult", String.valueOf(resp.data.roleId));
            }
            return resp;
        }), view);
    }

    @Override
    public void register(String phone, String passWord, String code, IBaseView<Void> view) {
        Map<String, Object> map = createMap(3);
        map.put("phone", phone);
        map.put("passWord", passWord);
        map.put("code", code);
        start(module.register(map), view);
    }

    @Override
    public void forGetPassWord(String phone, String code, String newPassWord, IBaseView<Void> view) {
        Map<String, Object> map = createMap(3);
        map.put("phone", phone);
        map.put("code", code);
        map.put("newPassWord", newPassWord);
        start(module.forgetPwd(map), view);
    }

    @Override
    public IModelCom createModel() {
        return ModelCom.getInstance();
    }
}

4.5 Use of pages

This is the calling method of the login page

public class LoginActivity extends AppCompatActivity {
    private ActivityLoginBinding view;
    private IContractCom.IPresenterCom presenterCom;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        view = ActivityLoginBinding.inflate(getLayoutInflater());
        setContentView(view.getRoot());
        presenterCom = new PresenterCom(this);
        view.btnLogin.setOnClickListener(v -> {
            String phone = view.edtPhone.getText().toString();
            String pwd = view.edtPwd.getText().toString();
            if (TextUtils.isEmpty(phone) || TextUtils.isEmpty(pwd)) {
                return;
            }
            presenterCom.login(phone, pwd, (b, i, s, data) -> {
                if (b) {
                    Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
                    startActivity(new Intent(this, MainActivity.class));
                    finish();
                } else {
                    Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
                }
            });
        });
    }
}

This is the calling method of the home page

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding view;
    private IContractUser.IPresenterUser presenterUser;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        view = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(view.getRoot());
        //在这种使用方法下,不需要手动解绑
        presenterUser = new PresenterUser(this);
        presenterUser.getUserInfo((b, i, s, data) -> {
            if (b) {
                String str = "用户名:" + data.userName + "\n生日:" + data.bir;
                view.tvMsg.setText(str);
            } else {
                Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

3. Other instructions

3.1 demo download address

mvp_demo

3.2 I have an idea

Generally, modifying the mobile phone number, logging in and registering will use the function of obtaining the verification code, and this function does not need to write the implementation logic once in the module of modifying the mobile phone number and the login and registration module. If necessary, directly obtain the verification code It might be better to make it into a separate module

public interface IContractPhoneCode {
    interface IPresenterPhoneCode extends IBasePresenter {
        void getCode(String phone);
    }
}

If you use it on the login page, use it like this

public class LoginActivity extends AppCompatActivity {
    private ActivityLoginBinding view;
    private IContractCom.IPresenterCom presenterCom;
    private IContractPhoneCode.IPresenterPhoneCode presenterPhoneCode;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        view = ActivityLoginBinding.inflate(getLayoutInflater());
        setContentView(view.getRoot());
        presenterCom = new PresenterCom(this);
        presenterPhoneCode = new PresenterPhoneCode(this);
        view.btnLogin.setOnClickListener(v -> {
            String phone = view.edtPhone.getText().toString();
            String pwd = view.edtPwd.getText().toString();
            if (TextUtils.isEmpty(phone) || TextUtils.isEmpty(pwd)) {
                return;
            }
            presenterCom.login(phone, pwd, (b, i, s, data) -> {
                if (b) {
                    Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
                    startActivity(new Intent(this, MainActivity.class));
                    finish();
                } else {
                    Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
                }
            });
        });
        view.btnGetCode.setOnClickListener(v->{
            String phone = view.edtPhone.getText().toString();
            presenterPhoneCode.getCode(phone,(b,i,s,d)->{
                
            });
        });
    }
}

3.3 Description

Knowledge is boundless, if there are any deficiencies in the text, I hope Haihan, if you can put forward your opinions, I will be very grateful.

Author: Liu Kunlin
Original link: (https://www.jianshu.com/p/415b46e810f2)

at last

If you want to become an architect or want to break through the 20-30K salary range, then don't be limited to coding and business, but you must be able to select models, expand, and improve programming thinking. In addition, a good career plan is also very important, and the habit of learning is very important, but the most important thing is to be able to persevere. Any plan that cannot be implemented consistently is empty talk.

If you have no direction, here I would like to share with you a set of "Advanced Notes on the Eight Major Modules of Android" written by the senior architect of Ali, to help you organize the messy, scattered and fragmented knowledge systematically, so that you can systematically and efficiently Master the various knowledge points of Android development.
insert image description here
Compared with the fragmented content we usually read, the knowledge points of this note are more systematic, easier to understand and remember, and are arranged strictly according to the knowledge system.

Full set of video materials:

1. Interview collection

insert image description here
2. Source code analysis collection
insert image description here

3. The collection of open source frameworks
insert image description here
welcomes everyone to support with one click and three links. If you need the information in the article, directly scan the CSDN official certification WeChat card at the end of the article to get it for free↓↓↓

Guess you like

Origin blog.csdn.net/weixin_43440181/article/details/129443075