Android开发-MVP框架学习记录

前言

MVP架构流行已久,但由于实在没时间(太懒),所以一直对MVP停留在听说一词上。

由于最近公司让我接收一个android项目,而该项目之前的android版本就是使用MVP架构,因此得以真正接触到了MVP架构。

本文章Demo点击下载

首先,我们来理解下MVP是什么?

MVP全称Model-View-Presenter,它把项目大概划分为3个模块,其实分别对应:

M层:实体层,负责获取实体数据。

V层:视图层,对应XML文件与Activity/Fragment

P层:逻辑控制层,同时持有ViewModel对象。

MVP的流程图大致如下所示:

那么,MVP有什么优势?

  1. 把业务逻辑抽离到Presenter层中,View层专注于UI的处理。

  2. 分离视图逻辑与业务逻辑,达到解耦的目的。

  3. Presenter被抽象成接口,可以根据Presenter的实现方式进行单元测试。

  4. 提高代码的阅读性。

  5. 可拓展性强。

同样,有优势就有缺点!

  1. 项目结构会对后期的开发和维护有一定影响,具体视APP的体量而定。

  2. 代码量会增多,如何避免编写过多功能相似的重复代码是使用MVP开发的一个重要处理问题。

  3. 有一定的学习成本。

趁热打铁,撸起袖子就是干!

下面通过输入账号密码实现登录的例子,来看看MVP到底是如何分工(甩锅)合作的。

创建一个空项目,名为MVPDemo好了。然后在项目下的mvpdemo文件夹中新增modelviewpresenter三个文件夹,分别对应实体层、视图层、逻辑控制层。然后在view文件夹中创建activity文件夹,并把MainActivity拖至其中。如下图所示:

activity_main.xml中添加三个组件,用于输入账号密码和登录

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.activity.MainActivity">

    <EditText
        android:id="@+id/uid"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="157dp"
        android:ems="10"
        android:hint="账号"
        android:inputType="textPersonName"
         />

    <EditText
        android:id="@+id/pwd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="219dp"
        android:ems="10"
        android:hint="密码"
        android:inputType="textPassword" />

    <Button
        android:id="@+id/login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="128dp"
        android:text="登录" />

回到MainActivity中,绑定三个UI组件

public class MainActivity extends AppCompatActivity {

    private EditText mUid,mPwd;
    private Button mLogin;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initUI();       //初始化UI
        initListener(); //初始化监听事件
    }

    private void initUI() {
        mUid.findViewById(R.id.uid);
        mPwd.findViewById(R.id.pwd);
        mLogin.findViewById(R.id.login);
    }

    private void initListener() {
        mLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
    }
}

接下来就算是开始用到MVP了,先来分析下登录操作,因为输入账号密码后需要进行登录操作,而登录操作可以看作有那么几步:

1.登录验证失败:账号密码不能为空,账号密码不能错误。如果存在这两种情况,则弹出对应Toast

2登录验证成功:弹出登录成功Toast,并跳转到登录成功页面

因此登录操作中既包括了逻辑控制层操作,也包括了视图层操作

那么我们就需要创建对应的文件

1.在presenter文件夹下创建接口类LoginActivityPresenterimpl文件夹;

2.在LoginActivityPresenter接口中新建一个登录方法;

3.接着在impl文件夹内创建LoginPresenterImpl类并实现LoginActivityPresenter接口的登录方法;

// 2.
package com.example.lee.mvpdemo.presenter;
public interface LoginActivityPresenter {
    void login(String uid, String pwd);
}
// 3.
package com.example.lee.mvpdemo.presenter.impl;
import com.example.lee.mvpdemo.presenter.LoginActivityPresenter;
public class LoginPresenterImpl implements LoginActivityPresenter {
    @Override
    public void login(String uid, String pwd) {
    }
}

LoginPresenterImpl中的完善login方法,其中,因为需要在验证操作后通知UI层弹出Toast操作,所以需要引入视图层的方法。所以在完善login方法前,先来添加视图层需要的方法:

1.在view文件夹下创建iView文件夹

2.在iView文件夹新建一个接口类ILoginActivity

3.在接口类ILoginActivity中添加两个方法,一个用于验证成功后返回,一个用于验证失败后返回

// 3.
package com.example.lee.mvpdemo.view.iView;
public interface ILoginActivity {
    //用于验证成功后返回
    void loginSuccess(int resultCode, String msg);
    //用于验证成失败后返回
    void loginFailed(int resultCode, String msg);
}

万事俱备,只欠login。我们回到LoginPresenterImpl类,往里面引入刚刚添加的接口类ILoginActivity,并完善登录验证失败/成功时的代码

package com.example.lee.mvpdemo.presenter.impl;
import android.text.TextUtils;
import com.example.lee.mvpdemo.presenter.LoginActivityPresenter;
import com.example.lee.mvpdemo.view.iView.ILoginActivity;
public class LoginPresenterImpl implements LoginActivityPresenter {
    
    private ILoginActivity mIView;
    public  LoginPresenterImpl(ILoginActivity iLoginActivity){
        mIView = iLoginActivity;
    }
    @Override
    public void login(String uid, String pwd) {
        System.out.println("触发:login"+" "+"uid:"+uid+" "+"pwd:"+pwd);
        if(TextUtils.isEmpty(uid)){
            //此时账号为空,告诉视图层需要展示调取loginFailed方法
            mIView.loginFailed(0,"账号不能为空");
        }else if(TextUtils.isEmpty(pwd)){
            //此时密码为空,告诉视图层需要展示调取loginFailed方法
            mIView.loginFailed(0,"密码不能为空");
        }else if(uid.equals("chaoiscool")  && pwd.equals("123456")){
            //此时验证成功,告诉视图层需要展示调取loginSuccess方法;
            //如果有保存用户信息的需要,也可以在此处告诉实体层,让它去进行保存用户信息,
            //操作和第6步似,例如:
            //mModel.saveUserBean(userBean)
            mIView.loginSuccess(1,"登录成功");
        }else {
            //此时账号或者密码错误,告诉视图层需要展示调取loginFailed方法
            mIView.loginFailed(1,"登录失败");
        }
    }
}

现在基本的设置已经完成,我们把注意力移到MainActivity身上,此时的MainActivity还是独自一人,还没和逻辑控制层、实体层和视图层建立联系。因此我们来给他们搭建一座桥梁:

1.引入逻辑控制层LoginPresenterImpl

private LoginPresenterImpl mLoginPresenter;

2.为LoginPresenterImpl新建实例对象

private LoginActivityPresenter getPresenter(){
   if(null == mLoginPresenter){
       mLoginPresenter = new LoginPresenterImpl(this);
   }
   return mLoginPresenter;
}

3.触发登录事件时,调用LoginPresenterImpl(逻辑控制层)里的login方法

private void initListener() {
    mLogin.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            System.out.println("触发:onClick");
            getPresenter().login(mUid.getText().toString(),mPwd.getText().toString());
        }
    });
}

4.实现接口类ILoginActivity(视图层)的两个方法,并在各自的中实现对应的逻辑

public class MainActivity extends Activity implements ILoginActivity
//登录成功回调
@Override
public void loginSuccess(int resultCode, final String msg) {
    System.out.println("触发:loginSuccess");
    System.out.println("触发:msg:"+msg);
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if(!TextUtils.isEmpty(msg)){
                Toast.makeText(MainActivity.this,msg,Toast.LENGTH_SHORT).show();
            }
        }
    });
}

//登录失败回调
@Override
public void loginFailed(int resultCode, final String msg) {
    System.out.println("触发:loginFailed");
    System.out.println("触发:msg:"+msg);
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if(!TextUtils.isEmpty(msg)){
                Toast.makeText(MainActivity.this,msg,Toast.LENGTH_SHORT).show();
            }
        }
    });
}

5.同样的,如果有保存用户数据的需要,也需要实现前面3、4的操作

总结

到这里为止,MVP的分工合作就已经配置完成。现在也应该对MVP的操作有一定程度的了解了。其

核心是逻辑控制层,所有的UI操作和数据操作,都是经过逻辑控制层来分派的。它的存在就像是你的BOSS,分派不同的任务给下属,专业的事情由专业的人来干!

最后放上效果图:

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