【Android工程之类】1 MVVM架构 - MVVM与单向数据流

1,798 阅读4分钟

前言

这个系列将讲述使用MVVM架构、LiveData、Room、Kodein、Retrofit、EventBus来建立一个统一的、优雅的、可维护的TODO程序,本系列分为多个章节,从0开始一步一步引入这些优秀的库。下图展示的是Jetpack组件库包含的内容,这套的架构方案的核心就是谷歌现在大力推荐的Jetpack作为基础,搭配外围脚手架补齐Jetpack短板,还原原汁原味的优雅Android。

多说一句,确保团队对架构有统一的理解是非常必要的,就好比开团要切adc一样自然,如此不论是bug修复还是功能开发都无需抽丝剥茧就能直接定位完成任务。

目录

• MVVM架构 - MVVM与单向数据流
• LiveData - 使用ViewModel绑定UI和点击事件
• EventBus - 全局事件分发
• Room - 丝滑的sqlite访问
• Retrofit - 丝滑的api接入
• Kodein - 引入依赖注入优雅生成ViewModel
• Service - 使用eventbus包装与service的通信
• Receiver和定时任务 - 安装卸载事件 各种manager的处理
• 低内存 - 组件被回收的正确处理
• Navigation - 优雅的Single Activity导航组件
• Paging - 分页组件

本系列我们以构建一个TODO APP为例,逐步分析这些这套架构是如何发挥作用的。首先看看Android开发者官网上的MVVM架构图。 这里强调一点,使架构干净整洁的精髓就是保持数据流单向流动。

• Activity / Fragment  
    View层,在这一层里,不要放和业务逻辑相关的任何代码,只编写处理UI逻辑的代码和函数,供ViewModel调用。当然,ViewModel是不能直接调用activity的,否则就破坏了单项数据流了,这里可以使用接口的方式把activity中的方法传入viewModel,当然更好的方式是使用EventBus进行事件分发。
• ViewModel
    保存UI相关的变量,可以与layout中的元素进行单向或双向的数据绑定。响应元素的onlick等事件,调用Repository完成任务,更改UI属性值,调用传入的回调接口或向eventbus发送事件来反馈用户操作是成功还是失败。ViewModel可以被多个Activity共享。
• Repository 
    对多个数据源的统一封装,形成统一方法,比如有saveUser(user: User)方法,内部调用远程api接口保存成功后,写入本地数据库,函数成功返回,但对调用者来说抽象了数据源的实现方法,减少上层逻辑处理负担。
• Model / Local Data Source 
    本地数据源抽象层
• Remote Data Source
    远程数据源抽象层

但是,我们的应用一般不可能这么简单,还会应用到service、receiver、后台进程什么的,这里为了简单起来,先不加入,等到后面对应的章节再补充进去。

起步

说了这么多废话,动手创建工程吧

工程创建

首先常规操作,创建一个名叫todo的工程

在module的build.gradle中加入

android {
    ...
    dataBinding {
        enabled = true
    }
}


dependencies {
    ...
    // architectures
    implementation"androidx.lifecycle:lifecycle-viewmodel-ktx:$archLifecycleVersion"
    implementation"androidx.lifecycle:lifecycle-livedata-ktx:$archLifecycleVersion"
    implementation"androidx.lifecycle:lifecycle-extensions:$archLifecycleVersion"
}

在project的build.gardle文件中统一声明版本

ext{
    archLifecycleVersion='2.2.0'
}

ViewModel创建

好了,一切就绪,我们首先建立我们的viewmodel,并声明一个hello字段,LiveData是个抽象类,我们使用MutableLiveData建立一个private的实例对象,并将其赋值给hello暴露给外部。 这里hello我们给个默认值"hello",该值可空,默认为null

class MainViewModel: ViewModel() {

    val _hello = MutableLiveData<String>("hello")
    val hello : LiveData<String> = _hello

}

在layout中绑定ViewModel

接下来修改layout文件,打开默认创建的layout文件,将其用layout标签包裹并加入data块,声明变量名为viewmodel,变量类型是刚才创建的class。viewmodel建议全小写,可以在activity中定义的viewModel区别开。

<?xml version="1.0" encoding="utf-8"?>

<layout 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">

    <data>
        <variable
            name="viewmodel"
            type="xyz.yuanxiaoqing.todo.MainViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewmodel.hello}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

在textview中我们使用@{}语法将其单向绑定到hello变量上

MainActivity

好了,最后一步,将viewmodel实例化绑定到layout中。这里直接实例化了MainViewModel,仅作为演示,后面章节将介绍如何优雅生成这个对象。这个ActivityMainBinding是什么?他是Android Studio自动根据R.layout.activity_main生成的Binding对象。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding: ActivityMainBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_main)
        val viewModel = MainViewModel()
        binding.apply {
            viewmodel = viewModel
        }
    }

}

ok,成功绑定。

本章所有代码已上传GitHub github.com/yxq2233234/…