一. 前言
看了很多关于MVP的文章,有初级篇,高级篇,还有终极篇 等等,给我的感觉就是: 有没有必要封装的那么复杂?
1. MVP主要解决了什么问题?
当页面比较复杂时: Activity 处理的东西太多,太杂,所以得按一定的规则分离出来.
2. 引发常见的问题?
① 内存泄露:
因为P层持有view层的引用, 目前常见的封装方式,基本都解决了这个问题。
② 新增的类过多:
例如:登陆页,ILoginView, LoginP, ILoginP(用于M层回调,非必须), loginM。
每个页面都要加这几个类, 反正我是写的挺烦的, 也有的兄弟把这个弄成插件,自动生成.
③一个V对应多个P(P层复用), 不太友好:
④ 学习成本相对比较高:
特别是:一些高级的封装方式,比如动态代理.
使用上: 需要关注的类和方法比较多,而且要一一对应.
封装逻辑比较复杂: 如果别人需要修改,花费时间相对会多一些.
正因为有了以上的问题,所以才有了后面MVVM模式. 这里就不说了.
MVP解决方案如下:
二. 先最终调用效果
public class LoginActivity extends MvpActivity<LoginPresenter>{
//...布局等代码忽略
@Override
public void init() {
presenter.login(); //登录
}
@Override
public void mvpSuccess(Object data, String type) {
//1.只有一个接口的话,直接强转对象
//2.多个接口回调的话, 根据type判断,强转对象
}
}
public class LoginPresenter extends MvpPresenter{
public LoginPresenter(ImvpView imvpView) {
super(imvpView);
}
public void login(){ //.....请求
//成功
mvpSuccess(new LoginResult("回调对象"), "loginType");
//失败
mvpFail(0, "登陆失败", "loginType");
}
}
说明:
1. 一个Presenter,一个接口的话, mvpSuccess回调,直接强转对象
2. 一个Presenter,多个接口的话, mvpSuccess回调, 根据返回的type强转对象.
3. 多个Presenter. 如下:mvpSuccess回调,也是根据type强转对象.
public class LoginActivity extends MvpActivity<LoginPresenter>{
TestPresenter testPresenter; //其它的 Presenter
@Override
public void init() {
testPresenter = new TestPresenter(this);
testPresenter.test(); //发起请求
}
@Override
protected void onDestroy() {
super.onDestroy();
if(testPresenter!= null){ //记得解绑
testPresenter.unbind();
}
}
}
1. 大伙觉得这样的使用方式怎么样呢? 虽然强转类型感觉不太友好,但是总体我觉得方便了很多.
2. 看到这里,可能还有很多疑问,例如,部分页面失败回调时候需要单独处理怎么办?
三. 封装过程(主要看V和P层, M层一样的道理)
1. 总的回调接口ImvpView
具体方法根据个人需要自定义, 例如:showLoading(String type), dissmiss(String type).
//回调接口
public interface ImvpView {
//成功
void mvpSuccess(Object data, String type);
//失败
void mvpFail(int code, String msg, String type);
}
2.MvpActivity类:
①: 抽象类,实现回调接口ImvpView.
②: 重写抽象方法,这样Activity页面需要什么方法就重写什么方法即可.
③: 定义一个泛型变量P, 这样无需每个Activity都定义变量P,P的初始化可以用反射, 也可以在具体Activity中new对象.
④: onDestroy解绑.
public abstract class MvpActivity<T extends MvpPresenter> extends BaseActivity implements ImvpView{
public T presenter;
public void layoutBefore(){
setPresenter();
}
private void setPresenter(){
//1. 懒的话可用反射直接创建, 子类无需再new
//2. 追求性能, 而且不觉得麻烦的话,可以子类new对象.
}
@Override
public void mvpFail(int code, String msg, String type) {}
@Override
protected void onDestroy() {
super.onDestroy();
if(presenter != null){
presenter.unbind();
}
}
}
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
layoutBefore();
setContentView(getLayoutId());
init();
}
public void layoutBefore(){}
public abstract int getLayoutId();
public abstract void init();
}
3.MvpPresenter类:
①: 主要思想, 提供给子类,调用ImvpView 里的方法,这个类只是加上的非空判断.
②: 提供方法:解除与Activity的关联。
③: 可扩展自己需要的方法,不会影响其它地方.
public class MvpPresenter{
ImvpView imvpView;
String defaultType = ""; //返回类型
public MvpPresenter(ImvpView imvpView){
this.imvpView = imvpView;
}
protected void mvpSuccess(Object data, String type){ //成功
if(imvpView == null){
return;
}
imvpView.mvpSuccess(data, type);
}
protected void mvpFail(int code, String msg, String type){ //失败
if(imvpView == null){
return;
}
imvpView.mvpFail(code, msg, type);
}
/**
* 解绑
*/
public void unbind(){
imvpView = null;
}
}
四: 总结
1. 学习成本低,使用简单, 封装逻辑简单,小白应该都能看懂.
调用时: 耦合性低了很多,V层只需关心需要哪些P, P层只需专心拿数据, 完全不需要关注其它类.
2. 解决了: 内存泄露,P的复用, 类过多 的问题
3. 扩展性: 在ImvpView, MvpActivity, MvpPresenter三个核心类, 新增功能, 其它地方完全不受影响.
4.不足: 强转类型感觉不太友好.
我做过的项目中,觉得这样封装暂时足够用了, 如有不足,欢迎指出。
最后附上: 反射初始化P的代码:
/**
* 初始化Presenter(觉得麻烦可忽略, 因为直接 new即可)
*/
private void setPresenter(){
try {
Type superClass = getClass().getGenericSuperclass();
Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
Class<?> clazz = getRawType(type);
Constructor constructor =clazz.getConstructor(ImvpView.class);
presenter = (T) constructor.newInstance(this);
} catch (Exception e) {
e.printStackTrace();
}
}
// type不能直接实例化对象,通过type获取class的类型,然后实例化对象
public static Class<?> getRawType(Type type) {
if (type instanceof Class) {
return (Class) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type rawType = parameterizedType.getRawType();
return (Class) rawType;
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
return Array.newInstance(getRawType(componentType), 0).getClass();
} else if (type instanceof TypeVariable) {
return Object.class;
} else if (type instanceof WildcardType) {
return getRawType(((WildcardType) type).getUpperBounds()[0]);
} else {
String className = type == null ? "null" : type.getClass().getName();
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or GenericArrayType, but <" + type + "> is of type " + className);
}
}
网友评论