Android 初识 MVC、MVP框架

转载请标明出处: http://blog.csdn.net/airsaid/article/details/51315096
本文出自:周游的博客

前言

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布局:

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:stretchColumns="1"
    tools:context="com.airsaid.mvcdemo.LoginActivity">

    <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="请输入用户名" />
    </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="请输入密码" />
    </TableRow>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:onClick="login"
        android:text="登录" />

</TableLayout>
  • LoginActivity代码:
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;
    }
}
  • UserBean:
public class User {
    public String username;
    public String password;
}

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

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

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;
        }
    }
}

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

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();
        }
    });
}

到这里一个简单版的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) 代码:

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();
            }
        });
    }
}

P (LoginPresenter)代码:

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();
    }

}

M(LoginModel)代码:

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;
        }
    }
}

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

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

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

 public LoginPresenter(LoginActivity view){
        this.mView = view;
 }

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

修改构参为接口:

 public LoginPresenter(IUserLoginView  view){
        this.mView = view;
 }

创建IUserLoginView接口:

package com.airsaid.mvpdemo.i;

/**
 * Created by zhouyou on 2016/5/4.
 */
public interface IUserLoginView {

    /**
     * 登录成功
     */
    void loginSuccess();


    /**
     * 登录失败
     */
    void loginError();
}

让LoginActivity去实现这个接口:

public class LoginActivity extends AppCompatActivity implements IUserLoginView

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

发布了55 篇原创文章 · 获赞 117 · 访问量 30万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览