简述
AAC 是Google官方提供的项目架构,它使开发者快速构建一款 健壮、易测、强壮 的应用。
为什么使用它
我们总在寻找最佳的应用架构,从早期的MVC到后来的MVP、MVVM。无疑我们使用它们的目的就是希望遵循一个架构原则 ,我们不希望将所有的代码都放入Activity或者Fragment中,任何与UI无关的代码都应该被剥离出去。我们希望的是各个组件各司其职,尽可能保证所有组件的职责单一性 。这样做无论是对我们程序的效率还是对于组件生命周期处理来说,都是非常友好并且高效的。
针对这一老生常谈的问题,Google 为我们隆重推出Android Architecture 官方认证架构,使用它,你将轻松解决 组件间交互、代码分层、生命周期管理、内存泄漏 等一系列随时可能让你崩溃的问题。
应用结构
在使用它之前,我们应当先了解构建一个 Android Architecture应用的结构,它看起来应该是这个样子:
UI --> ViewModel :Activity与Fragment作为UI层被置于整个架构最上方(最前端与用户交互),与之进行数据交流的仅有ViewModel层,也就是说Activity与Fragment中的一切UI变动都应当有ViewModel层进行控制,这也遵循了数据模型驱动UI 的架构原则。
ViewModel --> Repository:ViewModel层需要数据,那么就会有数据源,数据源也就是Repository层,你可以写很多的Repository类,在这个类中,它可以是通过 Retrofit 获取网络数据,亦或是通过Room获取本地SQL数据,哪种实现如何实现都不重要,只要 Repository 在ViewModel需求时返回给它需要的数据即可。
Repository --> Model :从Repository到model层,存在两种途径,Room获取本地数据库或者Retrofit(或其他网络请求方式)获取远程数据。Repository必须说明需要通过以上哪种方式去获取数据,接下来具体的获取数据的实现就交给 model层。
获取成功之后向上层返回数据(回调或者流式),最终UI层将数据表达出来。
引入依赖
project gradle 中
```
repositories {
google()
jcenter()
}
Module级 gradle 中
// ViewModel and LiveData
implementation"android.arch.lifecycle:extensions:1.1.0"
// alternatively, just ViewModel
implementation"android.arch.lifecycle:viewmodel:1.1.0"
// alternatively, just LiveData
implementation"android.arch.lifecycle:livedata:1.1.0"
annotationProcessor"android.arch.lifecycle:compiler:1.1.0"
// Room (use 1.1.0-alpha1 for latest alpha)
implementation"android.arch.persistence.room:runtime:1.0.0"
annotationProcessor"android.arch.persistence.room:compiler:1.0.0"
// Paging
implementation"android.arch.paging:runtime:1.0.0-alpha5"
// Test helpers for LiveData
testImplementation"android.arch.core:core-testing:1.1.0"
// Test helpers for Room
testImplementation"android.arch.persistence.room:testing:1.0.0"
```
你必须知道的组件
Lifecycle-Aware Components
由 Lifecycle ,LifecycleOwner 组成。
Lifecycle: 一个用来保存组件生命周期的状态信息的类,它允许其他对象观察这些状态信息。主要使用了Event 和 State 这两个枚举类来跟踪绑定的组件的生命周期状态信息。
LifecycleOwner: LifecycleOwner是一个接口,实现LifecycleOwner就表示这是个有生命周期的类,它有一个getLifecycle() 方法是必须实现的。Activity 和 Fragment(注:在AAC稳定的版本后) 都**已经实现了这个**接口,详细的实现可以自行查看代码。
ViewModel类
派生于ViewModel 的类,与特定的UI层 (Activity/Fragment类) 进行关联(数据交互),并且负责与数据的业务逻辑通信,比如调用其他的组件去加载数据。ViewModel与View解耦,且不受配置改变的影响,例如由旋转屏幕导致的重新创建Activity。同时,在做多Fragment间通信时,如果每个Fragment都持有一个Activity的ViewModel,那么它们便可以不通过Activity直接通信,并且开发者无需关注 Fragment 的生命周期。
LiveData
一个可以让数据具备可观察功能的类,持有可被观察的数据,它的写法是LiveData<T>。它使组件能够在不与 LiveData 存在明显依赖关系的前提下观察LiveData 中的数据的改变。LiveData 能感知带生命周期的组件的生命周期(但其本身不存在生命周期)并在相应状态作出相应处理,所以能有效防止对象内存泄漏。
Room
持久化库,在应用中负责在网络获取数据前的本地数据获取,使应用在网络状况不佳时也不至于无法使用
一个栗子
我们已有数据类 User ,它有一个属性 id:String
首先是View层
仅仅是在 UserFragment 中拿到了一个 viewModel 引用以及 得到一个传进来的 uid
ViewModel层
在 UserViewModel 中拿到一个 LiveData<User> 的引用 ,kotlin 为我们写好了get() 方法,并且我们配合使用lazy使LiveData<User> 在第一次被使用时赋值
在 View 层观察变化并消费
在 Fragment 中我们对 viewModel 实例中的 user 进行了观察,在 observe时传入实现了 LifecycleOwner 的此 Fragment (也就是this),使 user 可以感受到Fragment的生命周期,其次传入了消费user的操作。至此,user 一旦发生变化,UI也就可以跟着变化了。
接下来就是实现数据的变更了
按理说是应该ViewModel 层获取数据并赋值给我们的LiveData ,那么必然在 ViewModel 中我们就需要拿到 ApiService 或着 Dao ,但我们并不希望 ViewModel 的关注点变得如此繁重,所以我们加入新的一层 Repository ,所有的数据请求逻辑处理将由 Repository 进行并将结果给予ViewModel 。这样做的好处不仅仅是使ViewModel 职责单一化,同时也意味着我们在想要使用其他方式获取数据时,上层并不需要关心,所有的变动都在 Repository 内进行就ok。
新增Repository层
修改ViewModel层
最后,在UI 层初始化一下
至此,
我们使用AAC完成了一个从网络获取数据并更新UI的完整过程,当然这是没有进行任何封装的。然而AAC为我们提供的并不仅仅如此,我们还有一个重要的组件没有使用,那就是Room。
想象一个上百条数据的列表,我们可以使用上述方法在网络获取之后展示在 RecyclerView 中,这时我将应用kill掉,再次启动,应用又会再次请求网络数据,在数据返回之前我们的界面是空白的,即时我们在前一秒刚请求过一次列表数据。如果网络断开,那么更是导致我们的应用无法使用。这样的用户体验肯定是不佳的,所以我们使用Room,将网络数据存储在本地,在进行网络数据获取前先读区本地数据库。
接下来就开始使用Room
首先,用@Entity 注解去标注 User 类,表明它将作为数据库中的一张表,并未 id 添加主键注解
新建 MyDataBase 类派生于 RoomDatabase
注意:MyDataBase是抽象类,Room会为它自动提供一个实现
创建一个数据访问接口 Dao ,里面包含了我们对数据库的操作方法
可以发现这里查询数据的时候返回的是LiveData类型,因为Room知道数据库神恶魔时候被修改,当数据改变的时候它就会自动通知状态正为激活态的观察者们去更新数据或者UI。
在数据库类中引用Dao类
最后我们需要在 Repository 中加入 Room 的数据源
need-to-insert-img
这个例子的数据获取方式是先获取本地数据,成功之后再发起网络请求,实际项目时可能还要判断各种条件,例如网络状况。但不管如何变化,获取方法都是如上所示。
我在代码中省略掉了apiService 和 userDao 的具体实现,因为这两个对象都应是单例,我的做法是将其实现写在 Application 中。当然,你可以配合依赖注入框架 Dagger2 ,kodein 对它们进行实现,这也是我现在正在学习的。
☝️到这里,这个栗子就结束啦~
☝️在实际项目中我们AAC进行封装使用,就可以非常愉快地和我们的硬件玩耍了~