本文为译文,原文链接:
https://medium.com/@elye.project/android-architecture-components-for-dummies-in-kotlin-50-lines-of-code-29b29d3a381
以前写 android 程序时,只需要把所有代码写在 Activity 中就可以了,比如:
class MainActivity : AppCompatActivity() {
private var count = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
my_container.setOnClickListener { incrementCount() }
}
override fun onResume() {
super.onResume()
incrementCount()
}
private fun incrementCount() {
my_text.text = (++count).toString()
}
}
仅仅用小于 20 行的代码,你就可以实现一个计数程序,统计每次你点击和前台显示 App 的次数。
这样的代码虽然简单,但是却难以扩展成一个大型项目,就像上图中的小房子一样,有很多问题:
- 如果你旋转你的设备,count 会被重置为 0
- 这样的代码很难进行单元测试
- 把所有东西都放在一个 class 里面,会让开发者变笨变迟钝。也许你是个菜鸟,但你一定不想变成笨笨的开发者。
代码架构
有许多中方法可以更好地组织你的代码结构。最基本的方法就是把代码拆分成不同的 class ,我们称其为 关注点分离。
你可能听说过 MVP 、MVC 、MVVM…如果你很了解这些概念并且运用自如,那么你可以不用看这篇文章了,去喝杯咖啡然后干别的事情吧
如果不是的话,那么请继续读下去…
简单来说,通过把逻辑代码尽可能多地从 Activity 当中移到别的类当中就可以实现关注点分离。你地 Activity 只会去处理和 Android 本身相关地工作,以及 UI 的更新。让别的类来处理重要的逻辑事务。
这样的话。任何 LifeCycle 事件都由 Activity 通知 Logic class ,同时对于任何数据的改变,你的 Logic class 都会将其通知到 Activity 。
很简单吧?虽然听起来很简单,但是每个人具体实现的方式都不同。这就是 MVP 、MVC 、MVVM (我甚至听过 MVVMI)的由来。即使是同一种架构,代码写法也会有许多不同。
谷歌看不下去了
开发者使用各种架构、各种方法来分离视图和逻辑层,那么哪种是最好的呢?人们忍不住问谷歌。于是在 2017 的 Google IO 大会上,Google 决定提出他自己的解决方案。这就是我们说的 Android Architecture Components (安卓架构组件)。
如下图所示,看起来和前文的图几乎一样,但是有了更清楚的命名。
什么是 MVVM(Model View ViewModel)?
当你看见 ViewModel 这个词的时候,你可能会很容易的就联想到 MVVM 。但是 MVVM 到底是什么?
简单来说,它是基于 观察者 模式实现的架构。
在这种模式中,信息的提供者并不知道谁需要他去提供这些信息。但是对这些信息感兴趣的人会对其进行订阅,一旦信息发布,就可以获取到需要的信息。
就像你订阅一个 B 站 UP 主一样,他们一更新,你就能得到通知
这样,你大概就能猜到:
- ViewModel 通过 Activity 的 LifeCycle 对象订阅 Activity ,以便在 LifeCycleEvents 发生时得到通知
- Activity 通过ViewModel 的 LiveData 对象订阅 ViewModel ,以便在数据更新时得到通知
使用安卓架构组件
好了,理论已经谈得足够多了,现在让我们来看看怎么在代码中使用他们。
添加依赖
在我给出的这个例子中,在 app 的 build.gradle 中添加下面的依赖就足够了。还有很多其他的库,但是现在不需要它们。
implementation "android.arch.lifecycle:extensions:1.1.0"
ViewModel 类和它的 LiveData
依赖添加完之后,就创建一个 ViewModel 类
class MyViewModel(private var count: Int = 0) : ViewModel() {
val changeNotifier = MutableLiveData<Int>()
fun increment() { changeNotifier.value = ++ count }
}
为了能够更简单地在 Activity 中使用它,最好是继承自 ViewModel
。现在这个类中包含了count
变量,在我们点击或者 activity
出现在前台时,我们会用它进行 UI 的更新。除此之外,还包含了 名为 changeNotifier的 MutableLiveData
。只要count
发生改变,我仅需要相应地更新 changeNotifier.value
,不论是谁订阅了它,都能获取更新的数据。
在 MainActivity 中使用 ViewModel
完成了上述工作,现在我们需要把这个 ViewModel 类提供给我们的 MainActivity。
class MainActivity : AppCompatActivity() {
private val viewModel: MyViewModel by lazy {
ViewModelProviders.of(this).get(MyViewModel::class.java)
}
private val changeObserver =
Observer<Int> {
value -> value?.let { incrementCount(value) }
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.changeNotifier.observe(this, changeObserver)
my_container.setOnClickListener { viewModel.increment() }
}
private fun incrementCount(value: Int) {
my_text.text = (value).toString()
}
}
我们使用了lazy关键词来创建viewModel,这是谷歌推荐的方法
private val viewModel: MyViewModel by lazy {
ViewModelProviders.of(this).get(MyViewModel::class.java)
}
之后, 我们的代码写出了获取消息的行为以及获得消息后干什么。在 Kotlin 中,我们使用 lambda 表达式,这样会让代码看起来更干净。
private val changeObserver =
Observer<Int> {
value -> value?.let { incrementCount(value) }
}
之后我们必须使用下面的代码将 changeObserver
注册到viewModel
。
viewModel.changeNotifier.observe(this, changeObserver)
最后, 我们需要一个方法从 UI 去触发数据的变化。在这个例子中是通过点击事件。
my_container.setOnClickListener { viewModel.increment() }
从 MainActivity 到 ViewModel 再回到 MainActivity 就像是一趟往返的路途。我们并没有把所有的事情都放到 MainActivity 中来做,这样虽然看起来复杂了一点,但是我们却搭建了一个好的 App 架构。这样就能把逻辑代码全部转移到 ViewModel 中而不是 MainActivity 。
让 ViewModel 感知到 Activity 的生命周期变化
为了让 ViewModel 感知到 Activity 的生命周期变化,我们需要让它实现 LifeCycleObserver接口。
class MyViewModel(private var count: Int = 0) : ViewModel(),
LifecycleObserver {
val changeNotifier = MutableLiveData<Int>()
fun increment() { changeNotifier.value = ++count }
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() { increment() }
}
你的 viewModel
依赖于哪一个生命周期,就添加一个 @OnLifecycleEvent
注解。
在这个例子中,我们只对 ON_RESUME 感兴趣。
做完这步后,我们需要让viewModel
订阅 MainActivity
的Lifecycle
事件。在最新版本的 AppCompatActivity
类中,LifeCycle已经添加进来了。所以我们可以轻松地在onCreate
函数中实现如下代码:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.changeNotifier.observe(this, changeObserver)
lifecycle.addObserver(viewModel)
my_container.setOnClickListener { viewModel.increment() }
}
现在viewModel就不需要让MainActivity在每个生命周期事件发生时主动地调用viewModel的方法。只需要合适的@OnLifeCycleEvent注解,viewModel就会被通知到并且执行相应的逻辑。
妈妈再也不用担心我旋转设备了
现在我们可以轻松地扩展我们地 App ,在viewModel
中实现更复杂地逻辑。并且,相比于Activity,我们在可以在viewModel中更好地进行单元测试。
除此之外,最重要地一点时,当我们旋转设备时,count
不会再被重置为 0 。它的值被保存下来了,因为 Android 内部实现了在 ViewModel 中保存数据的机制,不需要你去显式地保存它。
但也不要高兴地太早
虽然 ViewModel 可以很好地保存数据,但是这并不意味着onSaveInstanceState就没有用武之地了。如果 Activity 被进程被系统杀死,那么count地值就不会被保存。
为了处理这个问题,我添加了处理savedInstanceState
的逻辑代码。
class MainActivity : AppCompatActivity() {
private val viewModel: MyViewModel by lazy {
ViewModelProviders.of(this).get(MyViewModel::class.java)
}
private val changeObserver = Observer<Int> {
value -> value?.let { incrementCount(value) }
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.restoreState(savedInstanceState)
viewModel.changeNotifier.observe(this, changeObserver)
lifecycle.addObserver(viewModel)
my_container.setOnClickListener { viewModel.increment() }
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
viewModel.saveState(outState)
}
private fun incrementCount(value: Int) {
my_text.text = (value).toString()
}
}
class MyViewModel(private var count: Int = 0) : ViewModel(),
LifecycleObserver {
companion object { const val COUNT_KEY = "CountKey" }
val changeNotifier = MutableLiveData<Int>()
fun increment() { changeNotifier.value = ++count }
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() { increment() }
fun saveState(outState: Bundle) {
outState.putInt(COUNT_KEY, count)
}
fun restoreState(inState: Bundle?) {
inState?.let { count = inState.getInt(COUNT_KEY) }
}
}
这样,不论是旋转设备还是进程被杀死,你的count值都可以被保留下来。
源码可以从以下链接获得:
https://github.com/elye/demo_android_architecture_components?source=post_page-----29b29d3a381
关于 ViewModel ,再说一点
可能上面最终的代码看起来比一开始直接在 Activity 中处理所有事情的代码要长很多。但是,如同画房屋设计图一样,想要实现更多细节,就必须有合理的组织架构,即把逻辑代码从 Activity 当中抽离出来。
我的观点是,如果你已经有熟悉的架构,并且运用自如,那么你就可以不用安卓架构组件, Android Architecture Componens 。除非它能为代码提供更好的表现,比如在杀死和恢复应用进程时保留变量值。
文末福利
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
一、架构师筑基必备技能
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
二、Android百大框架源码解析
1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程
三、Android性能优化实战解析
- 腾讯Bugly:对字符串匹配算法的一点理解
- 爱奇艺:安卓APP崩溃捕获方案——xCrash
- 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
- 百度APP技术:Android H5首屏优化实践
- 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
- 携程:从智行 Android 项目看组件化架构实践
- 网易新闻构建优化:如何让你的构建速度“势如闪电”?
- …
四、高级kotlin强化实战
1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》
-
从一个膜拜大神的 Demo 开始
-
Kotlin 写 Gradle 脚本是一种什么体验?
-
Kotlin 编程的三重境界
-
Kotlin 高阶函数
-
Kotlin 泛型
-
Kotlin 扩展
-
Kotlin 委托
-
协程“不为人知”的调试技巧
-
图解协程:suspend
五、Android高级UI开源框架进阶解密
1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
六、NDK模块开发
1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习
七、Flutter技术进阶
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)
…
八、微信小程序开发
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……
全套视频资料:
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓