Android MVP框架搭建

MVP框架搭建·源码
通过本文记录一下学习MVP三层架构的思想,并实现一个基础的MVP项目架构搭建过程,通过MVC架构设计一文了解到MVC存在内存泄漏的缺陷,而MVP通过弱应用可以很好的解决内存泄漏这个问题
本文通过用户登录案例实现MVP框架的搭建

image.png

MVP相对于MVC的区别在于,增加了Presenter协议层,通过Presenter协议层解耦了View层和Model层,MVP架构大致分为以下三种类型

  • 1.P接收V层的事件分发给M层进行业务处理
  • 2.Google官方给的MVP samples中P接收V层指令自己处理业务逻辑
  • 3.P既不自己处理,也不告知M层处理,而是告知具体的业务模块,比如网络请求,数据库操作等模块去执行相关业务等

具体选用那种看团队最终框架如何定义。

基类封装

通过弱引用避免了P层对V层的持有,当Activity finish的时候能够完全腾出内存占用

  • BaseModel
public abstract class BaseModel<P extends BasePresenter, CONTRACT> {
    protected P p;
    //业务结束,通过Presenter调用契约、合同(接口中的方法)void responseResult(T t)

    public BaseModel(P p) {
        this.p = p;
    }

    public abstract CONTRACT getContract();
}

  • BaseView
public abstract class BaseView<P extends BasePresenter, CONTRACT> extends Activity {
    protected P p;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //弱引用
        p = getPresenter();
        //绑定
        p.bindView(this);
    }

    //让P层做什么需求
    public abstract CONTRACT getContract();

    //从子类中获取具体的契约
    public abstract P getPresenter();

    //如果Presenter层出现异常,需要告知View层

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除绑定
        p.unBindView();
    }
}

  • BasePresenter
public abstract class BasePresenter<V extends BaseView, M extends BaseModel, CONTRACT> {

    protected M m;
    private WeakReference<V> vWeakReference;

    public BasePresenter() {
        m = getModel();
    }

    public void bindView(V v) {
        vWeakReference = new WeakReference<>(v);
    }

    public void unBindView() {
        if (vWeakReference != null) {
            vWeakReference.clear();
            vWeakReference = null;
            System.gc();
        }
    }

    //获取View,P--V
    public V getView() {
        if (vWeakReference != null) {
            return vWeakReference.get();
        }
        return null;
    }

    //获取子类具体契约(Model层和View层协商的共同业务)
    public abstract CONTRACT getContract();

    public abstract M getModel();
}
登录逻辑的实现
  • LoginContract
public interface LoginContract {
    interface Model {
        void executeLogin(String name, String pwd) throws Exception;
    }

    interface View<T extends BaseEntity> {
        //真实项目中们,请求结果往往是JavaBean
        void handlerResult(T t);
    }

    interface Presenter<T extends BaseEntity> {
        //登录请求(接收到View层的指令,可以自己做,也可以让Model层去执行)
        void requestLogin(String name, String pwd);

        //结果响应(接收到Model层处理的结果,通知View层刷新)
        void responseResult(T t);
    }
}
  • LoginModel
public class LoginModel extends BaseModel<LoginPresenter, LoginContract.Model> {

    public LoginModel(LoginPresenter loginPresenter) {
        super(loginPresenter);
    }

    @Override
    public LoginContract.Model getContract() {
        return new LoginContract.Model() {
            @Override
            public void executeLogin(String name, String pwd) throws Exception {
                //模拟网络请求
                if ("zhangsan".equalsIgnoreCase(name) && "123456".equalsIgnoreCase(pwd)) {
                    p.getContract().responseResult(new UserInfo("腾讯", "马化腾"));
                } else {
                    p.getContract().responseResult(null);
                }
            }
        };
    }
}
  • LoginPresenter
    LoginPresenter中展示了上述所说的三种类型的MVP架构方式
public class LoginPresenter extends BasePresenter<LoginActivity, LoginModel,
        LoginContract.Presenter> {
    @Override
    public LoginContract.Presenter getContract() {
        //既要履行View给他的需求,又要分配工作给Model去完成这个需求
        return new LoginContract.Presenter<UserInfo>() {
            @Override
            public void requestLogin(String name, String pwd) {
                //P层三种风格
                try {
                    //第一种:P层不做事情,只做转发,交给M层做
                    m.getContract().executeLogin(name, pwd);

                    //第二种:让功能模块做(Library:下载,请求,图片加载等功能模块)
                    //HttpEngine httpEngine = new HttpEngine<>(LoginPresenter.this);
                    //httpEngine.post(name, pwd);

                    //第三种:P层自己处理(google MVP samples就是这种)
                    /*if ("zhangsan".equalsIgnoreCase(name) && "123456".equalsIgnoreCase(pwd)) {
                        responseResult(new UserInfo("腾讯", "马化腾"));
                    } else {
                        responseResult(null);
                    }*/

                    //内存泄漏测试
                    /*new Thread(new Runnable() {
                        @Override
                        public void run() {
                            SystemClock.sleep(60000);
                        }
                    }).start();*/
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void responseResult(UserInfo userInfo) {
                //不管谁完成
                getView().getContract().handlerResult(userInfo);
            }
        };
    }

    @Override
    public LoginModel getModel() {
        return new LoginModel(this);
    }
}

最终LoginActivity实现登录逻辑的代码如下,代码简洁,易于后期维护

public class LoginActivity extends BaseView<LoginPresenter, LoginContract.View> {
    private EditText nameEt;
    private EditText pwdEt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        nameEt = findViewById(R.id.et_account);
        pwdEt = findViewById(R.id.et_password);
    }

    public void doLoginAction(View view) {
        String name = nameEt.getText().toString();
        String pwd = pwdEt.getText().toString();
        p.getContract().requestLogin(name, pwd);
    }

    @Override
    public LoginContract.View getContract() {
        return new LoginContract.View<UserInfo>() {
            @Override
            public void handlerResult(UserInfo userInfo) {
                if (userInfo != null) {
                    Toast.makeText(LoginActivity.this, userInfo.toString(), Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(LoginActivity.this, "登录失败!", Toast.LENGTH_SHORT).show();
                }
            }
        };
    }

    @Override
    public LoginPresenter getPresenter() {
        return new LoginPresenter();
    }
}

MVP框架搭建·源码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270