以前根据参考各路大神的博客,以及自己对mvp的理解,早早就写了这种不成熟的结合模式(旧版),历时半年,他们终于进化了!
一、基础介绍
1、什么是mvp
mvp是android开发架构之一,MVP每个字母分别代表Model、View和Presenter。
①model负责处理网络数据的处理
②presenter是model和view的桥梁,负责与两端的通信
③view是视图层
2、为什么有mvp
mvp的诞生得益于mvc,mvc确实贡献也不小而且也历经风雨,但mvc的耦合是在太严重,因此mvp就诞生了。在mvp模式下,model和view是不直接进行交互的,而是要通过presenter作为桥梁,这样一来,各层的分工就更加明确,
①model不需要管ui长什么样,只要把对应的数据给到p即可,至于p是直接给v还是处理了再给v,怎么给,都是p的事情。
②而p这里,p不管model用的post还是get,参数格式是什么,p只需要向m发起请求,并在拿到结果后,把结果回调给v即可,不需要管v是要弹出对话框还是吐司还是跳转页面。
③v只需要把用户的动作或输入的内容给到p,等待p回应即可!
3、这个mvp和其他有些不太一样
这个mvp主要还是表现在p有写与众不同,p层拿到v的实例后,通过动态代理得到视图实例,view方法的执行又是交给InvocationHandler来处理的,这样可以有效避免view不存在时,还执行view方法。P中推荐传入activity实例,其实要的是applicat和activity的name,暂时还没想到更好的方法,就这样用着先。如果传入的参数不是activity或者没传参数,记得要在页面退出的该回收的地方调用p的detachView方法。
二、实现步骤
1、基于哪些基础?
首先一定有个大前提,就是所有接口返回的数据格式,都是一样的,比如说现在我的接口返回数据格式是这样的
{
"code": 200,
"message": "提交成功"
}
又或者是这样的
{
"code": 200,
"message": "",
"data": {
"userName": "张三",
"headImage": "http://192.168.3.11/file/user/hf6d4g88a.jpg",
"sex": 1,
"bir": "2020-01-01"
}
}
他们都有些共同点,如code和message,那么data就是泛型了!所以定义的响应体接收的类为
public class CallResult {
public int code;
public String message;
public T data;
}
2、封装Retrofit
考虑到有时候需要在请求的header中加各种数据,比如说appVersion等,并且上传文件和普通的接口超时时间一般是不同的,因此就有了这种封装
2.1开始封装
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 map = new HashMap<>();
/**
* 自定义拦截器
*/
private final List> 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为什么要自定义解析?
当服务器返回的数据为
{
"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"
}
]
}
时,我们定义的实体类可以正常接收数据。如果接口返回的数据是
{
"code": 401,
"message": "登录状态已失效",
"data": null
}
的时候,你会发现直接json转换闪退,因为null无法转换成list,因此我们要自己定义解析工厂,以下是部分代码
@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、创建mvp的各种Base基类
3.1 model层BaseModel
OnHttpResultListener是自己定义的,用来接收接口参数,对调试非常好用,上线版本可以忽略
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 createService(String ip, Class 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 泛型
*/
protected Observable callBackOnUi(Observable observable) {
return observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* 发起请求,并且在新的子线程执行回调
*
* @param observable observable
* @param 泛型
*/
protected Observable callBackOnThread(Observable observable) {
return observable.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread());
}
}
3.2 视图层IBaseView
为了让ui实现这个回调更加简单,我这里将服务器返回的message和其他所有可能有的提示都通过s字段来返回了,因此不管是“登录状态已失效”还是“网络连接异常”,都是在s回调的。根据项目不用,有需要的可以拆分。
/**
* ui回调
*
* @param 泛型
*/
public interface IBaseView {
/**
* ui回调
*
* @param b 是否请求成功
* @param i 类型
* @param s 描述
* @param data 泛型
*/
void onCallBack(boolean b, int i, String s, T data);
}
3.3 Presenter层
这一层最关键的是IBasePresenter和它的实现类BasePresenter
3.3.1 IBasePresenter
start方法是发起请求的入口,使用时需要将model的方法传进去,然后跟视图绑定起来。
/**
* presenter需要具备的基础方法
*/
public interface IBasePresenter {
/**
* 开始发起请求
*
* @param observable model层返回的obs
* @param iBaseView 视图,回调
* @param 泛型
*/
void start(Observable> observable, IBaseView iBaseView);
/**
* 成功回调
*
* @param iBaseView 视图、回调
* @param data 数据
* @param 泛型
*/
void viewCallBackSuccess(IBaseView iBaseView, CallResult data);
/**
* 错误回调
*
* @param iBaseView 视图、回调
* @param e 错误信息
* @param 泛型
*/
void viewCallBackError(IBaseView iBaseView, Throwable e);
/**
* 解绑
*/
void detachView();
}
3.3.2 BasePresenter
BasePresenter处理一些回调,将一些非正常接口请求的结果转换成中文(指定描述)在回调给view,这里的所有数据都是可以自己定义的,另外如果在某种情况下需要弹窗退出登录,建议您新建一个MyBasePresenter extend BasePresenter,然后重写onTokenErrorCallBack()即可,但判断的逻辑需要更改一下。
public abstract class BasePresenter 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> mapView = new HashMap<>();
/**
* 视图引用
*/
private final Map> mapReference = new HashMap<>();
/**
* 请求对象
*/
private final Map 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 > void attachView(V view) {
if (view != null) {
WeakReference weakReference = new WeakReference(view);
mapReference.put(view.hashCode(), weakReference);
ClassLoader classLoader = view.getClass().getClassLoader();
Class>[] interfaces = view.getClass().getInterfaces();
InvocationHandler invocationHandler = new MvpViewHandler((IBaseView) weakReference.get());
IBaseView 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 泛型
* @return true存在,则回调,否则忽略
*/
protected boolean isViewAttached(IBaseView 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 boolean isSuccess(CallResult data) {
return data != null && data.code == SUCCESS;
}
/**
* 开始发起请求
*
* @param observable model层返回的obs
* @param baseView 视图、回调
* @param 泛型
*/
@Override
public void start(Observable> observable, IBaseView baseView) {
attachView(baseView);
mapDisposables.put(baseView.hashCode(), observable
.subscribe(data -> viewCallBackSuccess(baseView, data), e -> viewCallBackError(baseView, e)));
}
/**
* 成功回调
*
* @param view 视图、回调
* @param data 数据
* @param 泛型
*/
@Override
public void viewCallBackSuccess(IBaseView view, CallResult 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 泛型
*/
@Override
public void viewCallBackError(IBaseView 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 void _viewCallBackSuccess(IBaseView view, CallResult 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 void _viewCallBackError(IBaseView 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 void callBackError(IBaseView view, String message) {
view.onCallBack(false, 9000, message, null);
Log.e("excce", "UI回调错误信息:" + message);
}
/**
* 返回一个value类型为Object的哈希表
*
* @param initSize 大小
* @return Map
*/
protected Map createMap(int initSize) {
return new HashMap<>(initSize);
}
/**
* 返回一个value类型为Integer的哈希表
*
* @param initSize 大小
* @return Map
*/
protected Map createMapInt(int initSize) {
return new HashMap<>(initSize);
}
/**
* 返回一个value类型为String的哈希表
*
* @param initSize 大小
* @return Map
*/
protected Map createMapStr(int initSize) {
return new HashMap<>(initSize);
}
/**
* 登录状态失效,需要回到登录页
*
* @param message message
*/
protected void onTokenErrorCallBack(String message) {
}
/**
* 动态代理
*
* @param 泛型
*/
private class MvpViewHandler implements InvocationHandler {
private final IBaseView mvpView;
MvpViewHandler(IBaseView 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、开始使用
经过一系列漫长又复杂的封装,终于可以开始使用了,这里就以登录接口和获取用户信息接口为例,展示两个不同模块的使用方法
4.1 根据接口文档编写公共模块的IModelCom
这其实就是大家熟悉的Service
/**
* 公共方法模块
*/
public interface IModelCom {
/**
* 登录
*
* @param map phone,passWord
*/
@POST("api/user/login")
Observable> login(@Body Map map);
/**
* 注册
*
* @param map phone,code,passWord
*/
@POST("api/user/register")
Observable> register(@Body Map map);
/**
* 忘记密码
*
* @param map phone,code,newPassWord
*/
@POST("api/user/forget")
Observable> forgetPwd(@Body Map map);
}
另一个IModelUserCenter的写法差不多的,忽略。
4.2来看看实现类
是否在主线程回调,看自己咯
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> login(Map map) {
return callBackOnUi(api.login(map));
}
@Override
public Observable> register(Map map) {
return callBackOnUi(api.register(map));
}
@Override
public Observable> forgetPwd(Map map) {
return callBackOnUi(api.forgetPwd(map));
}
}
4.3 契约类
就目前看来,契约类只定义了P层,原因是m层已经是retrofit了,而v层又只有回调参数。
/**
* 公共模块契约类
*/
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 view);
/**
* 注册
*
* @param phone 手机号
* @param passWord 密码
* @param code 验证码
* @param view 回调
*/
void register(String phone, String passWord, String code, IBaseView view);
/**
* 忘记密码
*
* @param phone 手机号
* @param code 验证码
* @param newPassWord 新密码
* @param view 回调
*/
void forGetPassWord(String phone, String code, String newPassWord, IBaseView view);
}
}
4.4 P的实现
/**
* 公共P
*/
public class PresenterCom extends BasePresenter implements IContractCom.IPresenterCom {
public PresenterCom(Activity activity) {
super(activity);
}
@Override
public void login(String phone, String passWord, IBaseView view) {
Map 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 view) {
Map 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 view) {
Map 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 页面的使用
这是登录页的调用方法
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();
}
});
});
}
}
这是首页的调用方法
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.1 demo下载地址
mvp_demo
3.2 我有一个想法
一般修改手机号、登录和注册,都会用到获取验证码的功能,而这个功能并不需要在修改手机号的模块和登录注册模块都写一次实现逻辑,要是有需要,直接将获取验证码弄成单独的模块可能会更好
public interface IContractPhoneCode {
interface IPresenterPhoneCode extends IBasePresenter {
void getCode(String phone);
}
}
如在登录页使用的时候,就这样用
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 说明
知识无边无际,文中如有不足之处,还望海涵,若能提出您的高见,我将不胜感激。