Android 开发中的架构模式(1) -- MVC / MVP / MVVM

预备知识

  1. 了解 Android 基本开发

看完本文可以达到什么程度

  1. 了解如何分析一个架构模式
  2. 掌握 MVC,MVP,MVVM​ 架构定义和实现

文章概览

一、什么是架构

关于架构的定义,其实在很多书籍和文章中都是不同的,很难做一个统一。这里列举两个定义:
在维基百科里是这样定义的:
软件架构是一个系统的草图。软件架构描述的对象是直接构成系统的抽象组件。各个组件之间的连接则明确和相对细致地描述组件之间的通讯。在实现阶段,这些抽象组件被细化为实际的组件,比如具体某个类或者对象。
在 IEEE 软件工程标准词汇中是这样定义的:
架构是以组件、组件之间的关系、组件与环境之间的关系为内容的某一系统的基本组织结构,以及指导上述内容设计与演化的原理。

关于更多的定义,推荐阅读《软件架构设计:程序员向架构师转型必备》第二章
...

在看过茫茫多的架构定义以后,我理解的架构是这样的:

  1. 为了解决特定的问题而提出
  2. 按照特定的原则将系统整体进行模块/组件/角色的划分
  3. 建立模块/组件/角色间的沟通机制

具体解释一下,首先是要有特定的问题,没有问题空谈架构,仿佛是空中楼阁,没有实用价值,而对应到不同的问题,会有不同的解决方式。
其次是模块的划分要根据特定的原则,没有原则随意划分,也就无从去评估一个架构的好坏。最后就是模块间的通信机制,让系统成为一个整体。

最后,架构模式,其实更多的是一种思想,一种规则,往往一种架构模式可能会有不同的实现方式,而实现方式之间,只有合适与否,并没有对错之分

二、如何分析一种架构模式

上面我们介绍了架构的定义,根据这个定义,我们在后面分析架构模式的时候,也会从这三方面进行。

  1. 架构解决了什么问题
    知道了架构模式要解决的问题,我们才能针对性的去看,去想其解决方法是否得当,是否合适。
  2. 架构模式是如何划分角色的
    架构中最重要的就是角色 / 模块的划分,理解了架构模式中的角色划分,才能更好的理解其结构。
  3. 角色间是如何通信的
    角色间的通信也是重要的。相同的角色划分,采用不同的通信方式,往往就构成了不同的架构模式。
    角色间通信我们可以理解为数据的流向。在 Android 开发中,通信中的数据可以理解为两种,一种是数据结构,也就是网络请求,本地存储等通信使用的 JavaBean,另一种是事件,也就是控件产生的动作,包括触摸,点击,滑动等等。我们在通信过程中,也主要关注这两种数据。

三、常见的架构模式有哪些

对于我们 Android 开发者来说,常见的架构模式基本上就是 MVC,MVP,MVVM,这三种也是开发 GUI 应用程序常见的模式。
除此之外还有 分层模式,客户端-服务器模式(CS模式),主从模式,管道过滤器模式,事件总线模式 等等。
这篇文章还是具体分析 MVC,MVP,MVVM 这三种架构模式。

四、不使用架构之前 App 是怎么开发的

我们在了解架构的定义以后,可能会想,为什么要用这些架构模式呢?在我们不了解这些模式之前,也是一样的开发。类似设计模式,其实架构模式的目的不是为了让应用软件开发出来,而是让结构更清晰,分工更明确,扩展更方便等等。

我们可以看看,在不使用架构模式之前我们是怎么开发的。
举个简单的栗子,我们界面上有 EditText,TextView,Button 三个控件,要实现的功能也比较简单:

  1. EditText 接受用户输入的内容
  2. 处理用户输入的数据
  3. 数据处理后输出到 TextView 中
  4. 点击 Button 清空用户的输入

界面如下:

我们看看不使用架构模式是怎么开发的,也就是我们一般常用的开发方式:

  1. 首先在 xml 设计界面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/titleText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Normal" />

    <EditText
        android:id="@+id/edit"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:textColor="@android:color/darker_gray" />

    <TextView
        android:id="@+id/msgText"
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:layout_marginTop="10dp"
        android:text="default msg"
        android:textColor="@android:color/darker_gray" />

    <TextView
        android:id="@+id/clearText"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_marginTop="10dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="clear"
        android:textColor="@android:color/white" />
</LinearLayout>
  1. 在 Activity / Fragment 中获取 View,进行事件监听
  2. 通过 View 事件获取数据后进行处理
  3. 设置处理后的数据给 View 代码如下:
class NormalFragment : Fragment() {
    companion object {
        fun newInstance(): Fragment {
            return NormalFragment()
        }
    }
    private val handler: Handler = Handler()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.architecture, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        titleText.text = "NORMAL"
        edit.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                handleData(s.toString())
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {