LiveData
LiveData 是 Jetpack 提供的一种响应式编程组件,可以认为是轻量级的 RxJava,它可以包含任何类型的数据,并在数据发生变化的时候通知观察者。
LiveData 是一个可观察的数据持有者,和常规的 observable 不同,LiveData 是具有生命周期感知的,这意味着它能够在 Activity、Fragment、Service 中正确地处理生命周期,可确保 LiveData 仅更新处于活跃生命周期的应用组件观察者。
LiveData 的数据源一般是 ViewModel,也可以是其他可以更新 LiveData 的组件。LiveData 特别适合与 ViewModel 结合使用,大多数情况下它也是使用在 ViewModel 当中的。当数据更新后,LiveData 就会通知它的所有观察者。
LiveData 之所以能够成为 Activity 与 ViewModel 之间通信的桥梁,并且还不会有内存泄漏的风险,靠的是 Lifecycles 组件。LiveData 在内部使用了 Lifecycles 组件来自我感知生命周期的变化,从而可在 Activity 销毁时及时释放引用(不需要开发者在 onPause 或 onDestroy 方法中解除对 LiveData 的订阅),避免产生内存泄露的问题。
与 RxJava 的方法不同的是,LiveData 并不是通知所有观察者,它只会通知处于 Active 状态的观察者,如果一个观察者处于 Pause 状态或 Destroyed 状态,那么它将不会收到通知。
另外,由于要减少性能消耗,当 Activity 处于不可见状态时(比如息屏、或者被其他 Activity 遮挡),如果 LiveData 中的数据发生变化,是不会通知给观察者的。只有当 Activity 重新恢复可见状态时(Resumed),才会将数据通知给观察者(如果 LiveData 发生过多次数据变化,此时只会将最新的那份通知给观察者,前面的数据相当于已经过期了,会被直接丢弃。),而 LiveData 之所以能够实现这种细节的优化,依靠的还是 Lifecycles 组件。
基本用法
添加依赖:
build.gradle1
| implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1")
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class MainActivity : AppCompatActivity() {
private val TAG = "TAG_MainActivity"
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ...
val mutableLiveData = MutableLiveData<String>() mutableLiveData.observe(this,{ s -> Log.d(TAG, "onChanged: $s") }) mutableLiveData.postValue("测试") } }
|
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
class MainViewModel(countReserved:Int):ViewModel() {
var counter = MutableLiveData<Int>()
init { counter.value = countReserved }
fun plusOne(){ val count = counter.value ?: 0 counter.value = count + 1 } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| class MainActivity : AppCompatActivity() {
lateinit var viewModel: MainViewModel lateinit var sp: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) lifecycle.addObserver(MyObserver(lifecycle))
sp = getPreferences(Context.MODE_PRIVATE) val countReserved = sp.getInt("count_reserved",0)
viewModel = ViewModelProviders.of(this,MainViewModelFactory(countReserved)) .get(MainViewModel::class.java)
btnPlus.setOnClickListener{ viewModel.plusOne() }
viewModel.counter.observe(this) { count -> tvInfo.text = count.toString() } }
override fun onPause() { super.onPause() sp.edit { putInt("count_reserved",viewModel.counter.value?:0) } } }
|
还有比较规范的做法,是永远只暴露不可变的 LiveData 给外部。这样在非 ViewModel 中就只能观察 LiveData 的数据变化,而不能给其设置数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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 } }
|
如果想要在 LiveData 对象分发给观察者之前对其中存储的值进行更改,则可以使用 Transformations.map() 和 Transformations.switchMap() 。
map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class MainActivity : AppCompatActivity() {
private val TAG = "TAG_MainActivity"
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ...
val mutableLiveData = MutableLiveData<String>() mutableLiveData.observe(this,{ s -> Log.d(TAG, "onChanged1: $s") })
val transformedLiveData = Transformations.map<String, Any>(mutableLiveData ) { input -> input + " + transformedLiveData" }
transformedLiveData.observe(this,{ o -> Log.d(TAG, "onChanged2:$o") })
mutableLiveData.postValue("mutableLiveData")
} }
|
示例:
map() 的作用是将实际包含数据的 LiveData 和仅用于观察数据的 LiveData 进行转换。
1
| data class User(var firstName:String,var lastName:String,var age:Int)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class MainViewModel: ViewModel() {
private val userLiveData = MutableLiveData<User>()
lateinit var user: User
val userName : LiveData<String> = Transformations.map(userLiveData){ user -> "${user.firstName} ${user.lastName}" }
init { user = User("xiao","wangwang",20) userLiveData.value = user }
fun onPlus(){ user = User("wang","xiaoxiao",20) userLiveData.value = user } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class MainActivity : AppCompatActivity() {
lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java) btnPlus.setOnClickListener{ viewModel.onPlus() }
viewModel.userName.observe(this){ Log.e("TAG",it) } } }
|
示例2:
1
| data class Student(var name:String,var id:String,var score:Int)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class StudentViewModel : ViewModel() {
private var studentLiveData = MutableLiveData<Student>()
val _Student:LiveData<Int> get() = Transformations.map(studentLiveData){ it.score }
fun setStudentMessage(student: Student){ studentLiveData.value = student } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class StudentActivity : AppCompatActivity() { private lateinit var studentViewModel: StudentViewModel
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_student)
studentViewModel = ViewModelProvider(this).get(StudentViewModel::class.java) val student = Student("HH","123",90) studentViewModel.setStudentMessage(student)
studentViewModel._Student.observe(this, Observer { Log.d("TAG","score:$it") })
} }
|
switchMap
如果 ViewModel 中的某个 LiveData 对象是调用另外的方法获取的,那么便可以借助 switchMap() ,将这个 LiveData 对象转换成另外一个可观察的 LiveData 对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
object Repository {
fun getUser(userId:String):LiveData<User>{ val liveData = MutableLiveData<User>() liveData.value = User(userId,userId,0) return liveData } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
class MainViewModel: ViewModel() {
private val userIdLiveData = MutableLiveData<String>()
private val refreshLiveData = MutableLiveData<Any?>()
val user:LiveData<User> = Transformations.switchMap(userIdLiveData){ userId -> Repository.getUser(userId) }
val refreshResult = Transformations.switchMap(refreshLiveData){ Repository.refresh() }
fun getUser(userId:String){ userIdLiveData.value = userId }
fun refresh(){ refreshLiveData.value = refreshLiveData.value } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class MainActivity : AppCompatActivity() {
lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
btn.setOnClickListener{ val userId = (0..10000).random().toString() viewModel.getUser(userId) } viewModel.user.observe(this, Observer { user -> tvInfo.text = user.firstName }) } }
|
示例 2
1
| data class Student(var name:String,var id:String,var score:Int)
|
1 2 3 4 5 6 7 8 9 10 11 12
| class StudentRepository {
fun getStudentScore(id:String):LiveData<Int>{ val studentMutableLiveData = MutableLiveData<Int>() if (id == "1"){ studentMutableLiveData.value = 90 }else{ studentMutableLiveData.value = 80 } return studentMutableLiveData } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class StudentViewModel : ViewModel() {
private var studentIdLiveData = MutableLiveData<String>()
fun setStudentId(studentId:String){ studentIdLiveData.value = studentId }
var newScore:LiveData<Int> = Transformations.switchMap(studentIdLiveData){id-> StudentRepository().getStudentScore(id) } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class StudentActivity : AppCompatActivity() { private lateinit var studentViewModel: StudentViewModel
private lateinit var et:EditText private lateinit var tvScore:TextView private lateinit var btn:Button
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_student)
et = findViewById(R.id.et) tvScore = findViewById(R.id.tv_score) btn = findViewById(R.id.btn)
studentViewModel = ViewModelProvider(this).get(StudentViewModel::class.java) studentViewModel.newScore.observe(this, Observer { tvScore.text = "分数:$it" }) btn.setOnClickListener{ studentViewModel.setStudentId(et.text.toString().trim()) } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto">
<EditText android:id="@+id/et" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"/>
<TextView android:id="@+id/tv_score" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/et" app:layout_constraintStart_toStartOf="parent"/>
<Button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/tv_score" app:layout_constraintStart_toStartOf="parent" android:text="确定"/> </androidx.constraintlayout.widget.ConstraintLayout>
|
源码解析
LiveData 的本质是观察者模式,查看 observer 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @MainThread public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { assertMainThread("observe"); if (owner.getLifecycle().getCurrentState() == DESTROYED) { return; } LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); if (existing != null && !existing.isAttachedTo(owner)) { throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles"); } if (existing != null) { return; } owner.getLifecycle().addObserver(wrapper); }
|
1 2 3 4 5 6 7
| static void assertMainThread(String methodName) { if (!ArchTaskExecutor.getInstance().isMainThread()) { throw new IllegalStateException("Cannot invoke " + methodName + " on a background" + " thread"); } }
|
所以使用时需要确保 observe 方法是执行在主线程中的,且 LiveData 和生命周期相关联,如果当前状态是非活跃状态则不执行。
如果想让数据监测变化不受活动状态的影响,可以使用 observeForever 方法,这样 Activity 即使不处于活动状态,也可以接收到改变的数据,但当 Activity 销毁时,一定要主动调用 removeObserver 方法,否则 LiveData 会一直存在,导致内存泄漏。
接下来查看 LifecycleBoundObserver,其实现了 LifecycleEventObserver 接口,当页面发生改变时,程序会走到 onStateChanged 方法中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState(); if (currentState == DESTROYED) { removeObserver(mObserver); return; } Lifecycle.State prevState = null; while (prevState != currentState) { prevState = currentState; activeStateChanged(shouldBeActive()); currentState = mOwner.getLifecycle().getCurrentState(); } }
|
activeStateChanged 方法会调用 dispatchingValue 方法分发数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| void dispatchingValue(@Nullable ObserverWrapper initiator) { if (mDispatchingValue) { mDispatchInvalidated = true; return; } mDispatchingValue = true; do { mDispatchInvalidated = false; if (initiator != null) { considerNotify(initiator); initiator = null; } else { for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { considerNotify(iterator.next().getValue()); if (mDispatchInvalidated) { break; } } } } while (mDispatchInvalidated); mDispatchingValue = false; }
|
通过 considerNotify 方法更新数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private void considerNotify(ObserverWrapper observer) { if (!observer.mActive) { return; } if (!observer.shouldBeActive()) { observer.activeStateChanged(false); return; } if (observer.mLastVersion >= mVersion) { return; } observer.mLastVersion = mVersion; observer.mObserver.onChanged((T) mData); }
|
如果观察者已经不属于活动状态,则直接返回,并通过比较数据的版本号判断数据是否需要更新。如果需要更新则会回调到 observer 的 onChanged 方法中,从而实现在 UI 层接收数据的回调。
当调用 setValue 方法时,数据版本号会改变,并且会通过 dispatchingValue 方法进行数据处理,这样便实现了 LiveData 可观察的特性。
1 2 3 4 5 6 7
| @MainThread protected void setValue(T value) { assertMainThread("setValue"); mVersion++; mData = value; dispatchingValue(null); }
|
备注
参考资料:
LiveData 概览
第一行代码(第3版)
《Android 进阶指北》
《Android Jetpack开发 原理解析与应用实战》
欢迎关注微信公众号:非也缘也