生命周期管理库 (Lifecycles) 由三个组件构成,包括 Lifecycle、LiveData 和 ViewModel。它可以用来解决常见的生命周期问题
在介绍说正题之前,先来说说下啥是Jetpack,它跟AndroidX又有怎样的关联(不能一上来就瞎头巴脑学一通最后还不知道自己学的是哪个派系的,要有金字塔学习方式)。首先上个官方的定义:
Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法、减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者可将精力集中于真正重要的编码工作。
早期google官网有对Jetpack进行分类的(如下图),但目前看不到划分图片或说明了,原因应该是目前内容更丰富了不止是局限于此。
下面是自己对目前Jetpack库的一些划分和认知:
第一个是核心类,你也可以把它理解为基础类,也就是说我们一个最基本的 Android 工程都会默认依赖这些组件库
activity.*
、fragment*
、appcompat*
、core
、arch.core
、annotation
、collection
、Customview
等
第二个是架构组件,Jetpack 推出之后很令人兴奋的一点,就是 Google 引入了现代 Android 应用开发的架构指南,结合 MVVM 的架构设计,帮助我们轻松的处理 UI 与业务逻辑之间的关系
databinding*
、Lifecycle
、LiveData
、ViewModel
、startup
、room*
、paging*
、hilt*
、navigation*
、work*
、datastore
、savedstate
等
第三个是 UI 组件,这里需要说明一点,大多数的 UI 组件其实都包含着核心组件中的 appcompat * 中了,这里列出的是 Jetpack 中以独立组件库存在的 UI 组件
viewpage2
、compose
、drawerlayout
、coordinatorlayout
、recyclerview
、palette
、constraintlayout
、emoji
、transition
、swiperefreshlayout
、webkt
、browser
、Material Design Components *
、`dynamicanimation等
第四个是特殊业务组件,根据不同的业务场景,选择性使用
multidex
、camera*
、media2
、slice
、sharetarget
、preference
、window
、ads
等
第五个非移动端或者不常用组件,涉及游戏、车载、TV 等或平时极少使用的组件
car
、wear
、tvprovider
、games
、enterprise
、recommendation
(TV相关)、tracing
、
第六个是弃用的组件,有一些是因为官方不再更新维护了,有一些是在 Jetpack 中有更好的替代解决方案,如果我们的项目中还在使用这些组件库的话,建议尽快替换到最新的替代组件上
cardview(MaterialCardView替代)
、media(media2替代)
、gridlayout
、sqlite(room替代)
、percentlayout(coordinatorlayout替代)
、legacy
、loader
、localbroadcastmanager
第七个是用于测试的组件
benchmark
、test*
瞎BB了这么多,AndroidX嘞这是啥,不急不急稳住,这就徐徐道来。AndroidX包含了啥:其实就是如上说明的这些各个组件的实现包装每个功能都是放到AndroidX的包名下而已(命名空间),此外还有之前的support4、support7等各个support包的大统一,还有一些频繁更新和迭代的特性功能,使得在不同的 Android 版本和不同的设备上,实现行为一致的工作代码。说白了AndroidX是针对程序猿的一种说法,Jetpack是google对外宣传的一种说法,其实本质都是说的是一个东西,角度不一样而已
说明下:为毛这些功能不直接集成到Android系统SDK中呢?为毛还要单独放到AndroidX中呢?其实也很好理解,一般Android系统更新频率在半年或者一年更新一次,AndroidX可以频繁更新呀,有bug可以及时修复
作用:感知Android系统组件Activity、Service(LifecycleService)、Fragment、Application应用程序生命周期,解耦页面与组件。
说明作用之后,来说下为什么我们要使用它,而不是自己定义一个观察者,然后在对应的组件的生命周期的方法里调用自定义的观察者方法(前期google没推出这个组件我们就是这么干的,如下代码片)。
自定义缺陷:
1.自定义需要在组件的生命周期方法里添加很多额外的代码
2.代码入侵式强
class MyObserver{
fun activityStart(){}
fun activityStop(){}
}
class MainActivity:AppCompatActivity{
lateinit var observer:MyObserver
override fun onCreate(savedInstanceState: Bundle?) {
observer = MyObserver()
}
override fun onStart(){
super.onStart()
observer.activityStart()
}
override fun onStop(){
super.onStop()
observer.activityStop()
}
}
使用
1.定义一个观察者,继承LifecycleObserver
class MyObserver : LifecycleObserver{
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun activityStart(){
//监听生命周期方法后需要处理的动作
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun activityStop(){
//监听生命周期方法后需要处理的动作
}
}
说明:@OnLifecycleEvent注解类型一共有7种:ON_START、ON_START、ON_RESUME、ON_PAUSE、ON_STOP、ON_DESTORY、ON_ANY(表示任何生命周期方法都会回调)
2.添加监听
1.在Activity中使用
class MainActivity : AppCompatActivity{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(MyObserver())//就添加这一行代码就够了
}
}
说明:这里MainActivity
需要继承AppCompatActivity
,因为AppCompatActivity
实现了LifecycleOwner
接口,这也是我们规避掉了自定义实现需要自己手动添加代码触发逻辑的原理
2.在Application中使用
class MyApplication : Application{
override fun onCreate(){
super.onCreate()
ProcessLifecycleOwner.get().lifecycle().addObserver(new ApplicationObserver())
}
}
3.属于补充,非必须流程,如果需要在观察者对象中知道当前处于什么状态生命周期那么可以把Lifecycle
当做参数传递
class MyObserver(val lifecycle:Lifecycle):LifecycleObserver{
lifecycle.currentState//主动获取当前的生命周期状态
}
作用
1.专门用于存放页面相关的数据,减少Activity中的逻辑,提高类的可维护性和单元测试
2.保存瞬态数据的丢失(屏幕的旋转)
3.防止异步调用的内存泄漏
4.在多个Fragment之前共享数据(仅限于同一个Activity中的Fragment)
如上说明了4种作用,上一张官方的生命周期图以解疑惑(一图胜千语)
基本用法
1.添加依赖
dependencies{
implementation "andoridx.lifecycle:lifecycle-extensions:2.1.0"
}
2.创建一个与页面绑定相关数据的ViewModel类
class MainViewModel : ViewModel(){
var counter = 0 //用于计数
}
3.在页面中使用ViewModel类,获取数据
class Mainctivity : AppCompatActivity{
lateinit var viewModel : MainViewModel
override fun onCreate(savedInstanceState:Bundle?){
……
//这里必须使用ViewModelProviders.of(<你的页面实例>).get(<你的ViewModel>::class.java),不能使用new一个ViewModel的方式,因为ViewModel具有它自身独特的生命周期
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
plusOneBtn.setOnclickListener{
viewModel.counter++
refreshCounter()
}
refreshCounter()
}
private fun refreshCounter(){
infoText.text = viewModel.counter.toString()
}
}
4.如果ViewModel子类的构造函数存在传参的方式,这时候就需要使用工厂类的方式创建ViewModel了
class MainViewModel(countReserved:Int):ViewModel(){
var counter = countReserved
}
class MainViewModelFactory(countReserved:Int):ViewModelPrvider.Factory{
override fun <T: ViewModel> create(modelClass:Class<T>):T{
retrun MainViewModel(countReserved) as T
}
}
class Mainctivity : AppCompatActivity{
lateinit var viewModel : MainViewModel
override fun onCreate(savedInstanceState:Bundle?){
……
//这里必须使用ViewModelProviders.of(<你的页面实例>).get(<你的ViewModel>::class.java),不能使用new一个ViewModel的方式,因为ViewModel具有它自身独特的生命周期
viewModel = ViewModelProviders.of(this,MainViewModelFactory(countReserved)).get(MainViewModel::class.java)
}
}
进阶使用
1.ViewModel 配合数据绑定
与LiveData 单独结合使用
1.使用LiveData返回简单的数据
之前我们都是在Activity中手动获取ViewModel中的数据,但ViewModel却无法将数据的变化主动通知给Activity,所以接下来就引出了LiveData来解决这个问题。LiveData是一个可被观察的对象,在数据发送变化时可以自动触发通知给观察者。LiveData可用包含任何的数据类型。
class MainViewModel(countReserved:int):ViewModel(){
val counter = LiveData<Int>
get() = _counter
private val _counter = MutableLiveData<Int>()
init{
_counter.value = countReserved
}
fun plusOne(){
val count = _counter.value?:0
_counter.value = count + 1
}
fun clear(){
_counter.value = 0
}
}
class MainActivity : AppCompatActivity{
override fun onCreate(savedInstanceState:Bundle?){
……
plusOneBtn.setOnClickListener{
viewModel.plusOne()
}
clearBtn.setOnclickListener{
viewModel.clear()
}
//观察数据的变化
viewModel.counter.observe(this,Observer{count ->
infoText.text = count.toString()
})
}
}
//说明下:MutableLiveData是一种可变的LiveData,主要有3中读写数据的方法,分别是getValue()、setValue()在主线程中赋值、postValue()在非主线程中给LiveData设置数据
2.使用map和switchMap来变换LiveData返回获取的数据
//1.map使用场景
data class User(val firstName:String,var lastName:String,var age:Int)
class MainViewModel(countReserved:Int):ViewModel(){
val userLivedData = MutableLiveData<User>()
val userName : LiveData<String> = Transformations.map(userLivedData){user ->
"${user.firstName} #{user.lastName}"
}
}
//2.switchMap使用场景
//当某个LiveData对象是调用另一个方法获取,比如如下:
object Repository{
fun getUser(userId:String):LiveData<User>{
val liveData = MutableLiveData<User>()
liveData.value = User(userId,userId,0)//通常这个值从网络获取,这里只是简单的模拟
return liveData
}
}
class MainViewModel(countReserved:Int):ViewModel(){
……
//正确用法
private val userIdLiveData MutableLiveData<String>()
val user:LiveData<User> = Transformations.switchMap(userIdLiveData){userId->
Repository.getUser(userId)
}
fun getUser(userId:String){
userIdLiveData.value = userId
}
//错误用法
/**
* 原因:Repository.getUser(userId)内部每次都是重新new一个User对象,
* 所以在Activity中使用observe去观察该对象变换的话,无法感知数据的变化,
* 因为每次都是一个新的对象LiveData,是一个不可观察的
*/
fun getUser(userId:String):LiveData<User>{
return Repository.getUser(userId)
}
}
//说明:如果getUser方法没有入参,则可以直接使用自己给自身复制(userIdLiveData.value = userIdLiveData.value),
//然后就会自动刷新感知触发
与Data Binding结合使用,实现数据与UI双向绑定
作用
Data Binding
是一个通过观察数据变化来更新 UI 的组件库。通过 ViewModel
、LiveData
和 Data Binding
的组合,您可以移除以往给 LiveData 添加观察者的做法,改为直接在 XML 中绑定View Model
和 LiveData
使用
1.在 Android Studio 中 build.gradle 配置开启 Data Binding
dataBinding {
enabled = true
}
2.Data Binding
布局使用layout包裹
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="viewmodel"
type="com.android.MyViewModel"/>
data>
<TextView
android:id="@+id/userName"
android:text="@{viewmodel.userName}"