您现在的位置:首页 >> Android开发 >> 内容

Android 初识 MVC、MVP框架

时间:2016-5-11 9:34:21 点击:

  核心提示:前言MVC、MVP、MVVP相信大家已经耳熟能详了,作为Android最出名的三个框架,它们的应用是非常的广泛。这篇博客就来简单介绍下其中二种框架。也加强下自己对这方面的了解。由于自己菜鸟一枚,有不对...

前言

MVC、MVP、MVVP相信大家已经耳熟能详了,作为Android最出名的三个框架,它们的应用是非常的广泛。这篇博客就来简单介绍下其中二种框架。也加强下自己对这方面的了解。由于自己菜鸟一枚,有不对和需要补充的地方欢迎评论~

MVC

MVC全名是:Model(模型) View(视图) Controller(控制器) 是软件架构中最常见的框架,简单来说,就是通过Controller的控制去操作Model层的数据,并且返回给View作展示,具体见下图:

当用户触发了一个事件,View层会将这个事件发送给Controller控制层,然后Controller会通知Model层更新数据,Model层更新完数据之后会直接显示在View层上,这就是MVC的工作原理了。

说到这里可能就有人会说了,明明一个Activity就能搞定的事,为什么还分三个类来写,烦不烦?

So,那为什么要采用架构设计呢?

通过设计,可以使我们的程序模块化,做到模块内部的高聚合和模块之间的低耦合。这样做的好处是,当我们在开发中只需专注一点即可!提高程序的开发效率,并且更容器做后期的测试以及定位问题。但是,我们不能为了设计而设计,为了架构而架构。对于不同量级的工程,具体的架构实现方法必然是不同的。

在Android中,MVC的应用无处不在,如:我们经常写的layout.xml就对应MVC的View层,里面都是一些View的布局代码,而各种JavaBean和Adapter就类似与MVC中的Model层,Activity就是Controller层了。下面用一个登录的小例子来演示一下。

首先看下常规写法,把所有逻辑都放在Activity里面的:
* activity_login.xml布局:
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--?xml version="1.0" encoding="utf-8"?-->
<tablelayout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" tools:context="com.airsaid.mvcdemo.LoginActivity" android:stretchcolumns="1" android:paddingtop="@dimen/activity_vertical_margin" android:paddingright="@dimen/activity_horizontal_margin" android:paddingleft="@dimen/activity_horizontal_margin" android:paddingbottom="@dimen/activity_vertical_margin" android:layout_width="match_parent" android:layout_height="match_parent">
 
    <tablerow>
 
        <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="用户名:">
 
        <edittext android:id="@+id/et_login_username" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入用户名">
    </edittext></textview></tablerow>
 
    <tablerow>
 
        <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="密码:">
 
        <edittext android:id="@+id/et_login_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入密码">
    </edittext></textview></tablerow>
 
    </tablelayout><button android:onclick="login" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="登录" android:layout_margintop="10dp"></button>
LoginActivity代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<code class="hljs xml">
<code class="hljs java">public class LoginActivity extends AppCompatActivity {
 
    private EditText mUsername;
    private EditText mPassword;
    private ProgressDialog dialog;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        initView();
    }
 
    private void initView() {
        dialog = new ProgressDialog(this);
        mUsername = (EditText) findViewById(R.id.et_login_username);
        mPassword = (EditText) findViewById(R.id.et_login_password);
    }
 
    // 登录
    public void login(View v){
        String username = mUsername.getText().toString();
        String password = mPassword.getText().toString();
        final User user = new User();
        user.username = username;
        user.password = password;
 
        if(checkUser(user)){
            dialog.show();
            // 模拟请求登录接口
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 模拟耗时操作
                    SystemClock.sleep(2000);
 
                    if ("123".equals(user.username) && "123".equals(user.password)){
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                onLoginSuccess();
                            }
                        });
 
                    }else{
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                onLoginError();
                            }
                        });
                    }
                }
            }).start();
        }else{
            Toast.makeText(getApplicationContext(), "用户名或密码不能为空", Toast.LENGTH_SHORT).show();
        }
    }
 
    /**
     * 登录成功
     */
    private void onLoginSuccess() {
        Toast.makeText(getApplicationContext(), "登录成功", Toast.LENGTH_SHORT).show();
        dialog.dismiss();
    }
 
    /**
     * 登录失败
     */
    private void onLoginError() {
        Toast.makeText(getApplicationContext(), "登录失败", Toast.LENGTH_SHORT).show();
        dialog.dismiss();
    }
 
    /**
     * 判断用户输入的信息是否正确
     * @param user
     * @return
     */
    private boolean checkUser(User user) {
        // 在这里由于演示,所以只做简单非空判断,实际开发中会判断更多情况
        if(TextUtils.isEmpty(user.username) || TextUtils.isEmpty(user.password)){
            return false;
        }
        return true;
    }
}</code></code>
UserBean:
?
1
2
3
4
5
<code class="hljs xml">
<code class="hljs cs">public class User {
    public String username;
    public String password;
}</code></code>

这是一个超级简化版的登录,但是差不多已经有上百行代码了,如果都写出来的话,那么就太多了,所以我们就需要遵循某种模式来进行拆分,下面是用MVC模式进行拆分后的代码:

新增一个类,将请求网络的操作抽取出来(这里由于模拟请求网络所以代码并不多):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<code class="hljs xml">
<code class="hljs java">public class LoginModel {
 
    /**
     * 请求网络,发送登录消息
     * @return
     */
    public boolean sendLoginInfo(User user){
        SystemClock.sleep(2000);
        if ("123".equals(user.username) && "123".equals(user.password)){
            return true;
        }else{
            return false;
        }
    }
}</code></code>

修改Activity中的请求网络为通过创建Model调用方法去判断登录是否成功:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<code class="hljs xml">
<code class="hljs java">LoginModel loginModel = new LoginModel();
if(loginModel.sendLoginInfo(user)) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            onLoginSuccess();
        }
    });
}else{
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            onLoginError();
        }
    });
}</code></code>

到这里一个简单版的MVC抽取就完成了,其实代码并没有少很多,这是因为Activity存在了两部分内容,一部分是业务相关的,一部分是界面相关的。在这里问题就产生了,如果界面复杂或业务多的话,会导致Activity里面的东西会很庞大,V里面的信息就只有一个Layout,而C也就是Activity里的东西则异常庞大。

那么这个时候,我们可以选择将业务部分的代码进行拆分,其对应的模式就是:MVP。
如果将Activity中界面相关的内容进行拆分,其对应的模式就是MVVM。

下面简单讲解一下MVP模式,MVVM由于涉及到data binding框架,东西比较多,下次再写。

MVP

MVP是从经典的MVC中演变过来的,在思想上,它们有想通的地方:
Controller/Presenter负责逻辑的处理,Model用于提供数据,View负责显示。

原来的Model层不变,Activity合并到View层,Presenter用于链接View层和Model层。
下面用MVP模式重构上面的登录小例子:
布局文件和Bean如上,就不贴了。

分包:

注意:Activity并不是View,setContextView()中的xml文件才是。

V (Activity) 代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<code class="hljs xml">
<code class="hljs java">public class LoginActivity extends AppCompatActivity {
 
    private EditText mUsername;
    private EditText mPassword;
    private ProgressDialog mDialog;
    private LoginPresenter mPresenter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
 
        mUsername = (EditText) findViewById(R.id.et_login_username);
        mPassword = (EditText) findViewById(R.id.et_login_password);
        mDialog = new ProgressDialog(this);
        mPresenter = new LoginPresenter(this);
    }
 
    public void login(View v){
        String username = mUsername.getText().toString();
        String password = mPassword.getText().toString();
        final User user = new User();
        user.username = username;
        user.password = password;
        if(mPresenter.checkUserInfo(user)){
            mDialog.show();
            mPresenter.login(user);
        }else{
            Toast.makeText(getApplicationContext(), "用户名或密码不能为空", Toast.LENGTH_SHORT).show();
        }
    }
 
    /**
     * 登录成功
     */
    public void loginSuccess() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "登录成功", Toast.LENGTH_SHORT).show();
                mDialog.dismiss();
            }
        });
    }
 
    /**
     * 登录失败
     */
    public void loginError(){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "登录失败", Toast.LENGTH_SHORT).show();
                mDialog.dismiss();
            }
        });
    }
}</code></code>

P (LoginPresenter)代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<code class="hljs xml">
<code class="hljs java">public class LoginPresenter {
 
    private LoginActivity mView;
 
    public LoginPresenter(LoginActivity view){
        this.mView = view;
    }
 
    /**
     * 校验用户信息
     * @param user 用户信息
     * @return true:校验成功 false:校验失败
     */
    public boolean checkUserInfo(User user){
        // 在这里由于演示,所以只做简单非空判断,实际开发中会判断更多情况
        if(TextUtils.isEmpty(user.username) || TextUtils.isEmpty(user.password)){
            return false;
        }
        return true;
    }
 
    /**
     * 登录
     * @param user 用户信息
     */
    public void login(final User user){
        new Thread(new Runnable() {
            @Override
            public void run() {
                LoginModel loginModel = new LoginModel();
                if(loginModel.sendLoginInfo(user)){
                    mView.loginSuccess();
                }else{
                    mView.loginError();
                }
            }
        }).start();
    }
 
}</code></code>

M(LoginModel)代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<code class="hljs xml">
<code class="hljs java">public class LoginModel {
 
    /**
     * 请求网络,发送登录消息
     */
    public boolean sendLoginInfo(User user) {
        // 模拟请求网络操作
        SystemClock.sleep(2000);
        if ("123".equals(user.username) && "123".equals(user.password)) {
            return true;
        } else {
            return false;
        }
    }
}</code></code>

至此,一个简单的MVP模式的登录小例子就出来了,相比于MVC,MVP中的代码已经相当简洁了,各模块之间分工明确。
View层负责和UI进行交互,Model层负责提供数据,Presenter层负责处理业务逻辑并处理Model层和View层的通信。

相信大家也看出来了,MVP主要解决就是把逻辑层给抽出来成P层,还有就是不和MVC中的Molder层可以操作View层一样,而是由Presenter负责通信的角色。这样做的好处是,如果有逻辑上的修改,那么我们直接修改Presenter层的东西就可以了。

到这里,并没有完善,我们还可以扩展下,上面的LoginActivity的构参中,我们传入了一个LoginActivity:

?
1
2
3
4
<code class="hljs xml">
<code class="hljs cs"> public LoginPresenter(LoginActivity view){
        this.mView = view;
 }</code></code>

这里其实并不合理,因为LoginActivity是属于V层的东西,而我们在开发过程中,V层不光会有Activity,也会有Fragment,如果按照上面写,通用性并不好,这个时候,接口开发就派上用场了。

修改构参为接口:

?
1
2
3
4
<code class="hljs xml">
<code class="hljs cs"> public LoginPresenter(IUserLoginView  view){
        this.mView = view;
 }</code></code>

创建IUserLoginView接口:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<code class="hljs xml">
<code class="hljs java">package com.airsaid.mvpdemo.i;
 
/**
 * Created by zhouyou on 2016/5/4.
 */
public interface IUserLoginView {
 
    /**
     * 登录成功
     */
    void loginSuccess();
 
 
    /**
     * 登录失败
     */
    void loginError();
}</code></code>

让LoginActivity去实现这个接口:

?
1
2
<code class="hljs xml">
<code class="hljs java">public class LoginActivity extends AppCompatActivity implements IUserLoginView</code></code>

到这里就扩展完成了,这只是一个MVP的简单扩展,具体大家可以看看Android官方的MVP架构实例项目。
在这里推荐一篇相关文章,大家感兴趣可以看看:Android官方MVP架构实例项目解析

作者:不详 来源:网络
  • 上一篇:Android 界面刷新
  • 下一篇:没有了
  • 共有评论 1相关评论
    发表我的评论
    • 大名:
    • 内容:
  • 青春网络编程技术(www.qcfupo.com) © 2020 版权所有 All Rights Reserved.
  • Powered by qcfupo! V3.0sp1
  • 百度友情链接: 百家乐[www.chinawestnews.net/aomenbaijiale/] 赌博网[www.chinawestnews.net/amdbwz/] 海立方[www.bjl.gov.cn/bjgylc/] 娱乐城[www.gtxrmzf.gov.cn/mapai/] 娱乐城[www.hfsf.gov.cn/yfgj/] 威尼斯人[www.hllinkou.gov.cn/baijile/] 澳门威尼斯[www.jjwgj.gov.cn/wnsr/] 888真人[www.lagm.gov.cn/888zhenren/] 百乐坊[www.laoling.gov.cn/blfylc/] 现金网[www.longyu.gov.cn/xjw/] 百家乐[www.chinawestnews.net/ambjl/] 新葡京娱乐场[www.sjzedu.gov.cn/jsylc/] 时时彩[www.snjw.gov.cn/ssc/] 赌博[www.chinawestnews.net/aomendubo/]