LiveData 是一个数据持有类,它持有一个值并且该值可以被观察。不同于普通的可观察者,LiveData 遵从应用组件的生命周期,这样 Observer 便可以指定一个其应该遵循的 Lifecycle。
如果 Observer 所依附的 Lifecycle 处于 STARTED 或者 RESUMED 状态,则 LiveData 认为 Observer 处于活跃状态。
避免内存泄露:因为 Observer 是绑定到 Lifecycle 对象上的,当 Lifecycle 对象被销毁的时候,LiveData 对象也会被自动清除
资源共享:通过单例模式,可以在多个 Activity 或者 Fragment 之间共享 LiveData 数据。
不再手动的处理生命周期:Fragment 只有在处于活跃的时候才会观察 LiveData 数据。由于 Fragment 提供了 Lifecycle 对象,所以 LiveData 会管理这一切
始终保持最新的数据:如果一个 Lifecycle 重新启动以后(例如:Activity 从后台重新开始运行于前台),它会接收到最新的数据(除非没有最新的数据)
正确处理配置改变:如果一个 Activity 或者 Fragment 以为配置改变(例如:旋转屏幕)被重建以后,LiveData 将会接收到最新的数据
不会因为 Activity 停止而使应用崩溃:如果 Observer 所绑定的 Lifecycle 处于闲置状态(例如:Activity 处于后台运行时),他们不会接收到改变的事件
LiveData没有setValue方法,所以用MutableLiveData
setValue只能在main线程调用,postValue可以在子线程调用
视图层(Activity 或者 Fragment)与 ViewModel 层进行通讯的一种便捷的方式就是使用 LiveData
来进行观察。这个视图层订阅 Livedata 的数据变化并对其变化做出反应。这适用于连续不断显示在屏幕的数据.
但是,有一些数据只会消费一次,就像是 Snackbar 消息,导航事件或者对话框。
/** * A lifecycle-aware observable that sends only new updates after subscription, used for events like * navigation and Snackbar messages. * * * This avoids a common problem with events: on configuration change (like rotation) an update * can be emitted if the observer is active. This LiveData only calls the observable if there's an * explicit call to setValue() or call(). * * * Note that only one observer is going to be notified of changes. */ class SingleLiveEvent: MutableLiveData () { private val pending = AtomicBoolean(false) @MainThread override fun observe(owner: LifecycleOwner, observer: Observer ) { if (hasActiveObservers()) { // Log.d("SingleLiveEvent","Multiple observers registered but only one will be notified of changes.") } // Observe the internal MutableLiveData super.observe(owner, Observer { t -> if (pending.compareAndSet(true, false)) { observer.onChanged(t) } }) } @MainThread override fun setValue(t: T?) { pending.set(true) super.setValue(t) } /** * Used for cases where T is Void, to make calls cleaner. */ @MainThread fun call() { value = null } }
问题 : SingleLiveEvent 的问题在于它仅限于一个观察者。如果您无意中添加了多个,则只会调用一个,并且不能保证哪一个
AtomicBoolean
AtomicBoolean,在这个Boolean值的变化的时候不允许在之间插入,保持操作的原子性。方法和举例:compareAndSet(boolean expect, boolean update)。这个方法主要两个作用 1. 比较AtomicBoolean和expect的值,如果一致,执行方法内的语句。其实就是一个if语句 2. 把AtomicBoolean的值设成update 比较最要的是这两件事是一气呵成的,这连个动作之间不会被打断,任何内部或者外部的语句都不可能在两个动作之间运行。为多线程的控制提供了解决的方案
val pending = AtomicBoolean(false) if (pending.compareAndSet(true, false)) { observer.onChanged(t) } 1:比较pending(是false)和expect(true)是否一致,以上pending.compareAndSet(true, false)是不一致,所以pending.compareAndSet(true, false)为false,不走if里面 2:如果pending.compareAndSet(false, true) 比较pending(是false)和expect(false)是否一致,以上pending.compareAndSet(true, false)是一致,所以pending.compareAndSet(true, false)为true,并且pending会置为update(true),再走if里面
/** * Used as a wrapper for data that is exposed via a LiveData that represents an event. */ open class Event(private val content: T) { var hasBeenHandled = false private set // Allow external read but not write /** * Returns the content and prevents its use again. */ fun getContentIfNotHandled(): T? { return if (hasBeenHandled) { null } else { hasBeenHandled = true content } } /** * Returns the content, even if it's already been handled. */ fun peekContent(): T = content }
class ListViewModel : ViewModel { private val _navigateToDetails = MutableLiveData>() val navigateToDetails : LiveData > get() = _navigateToDetails fun userClicksOnButton(itemId: String) { _navigateToDetails.value = Event(itemId) // Trigger the event by setting a new Event as a new value } }
myViewModel.navigateToDetails.observe(this, Observer { it.getContentIfNotHandled()?.let { // Only proceed if the event has never been handled startActivity(DetailsActivity...) } })
这种方法的优点在于用户使用 getContentIfNotHandled()
或者 peekContent()
来指定意图。这个方法将事件建模为状态的一部分:他们现在只是一个消耗或者不消耗的消息。
使用事件包装器,您可以将多个观察者添加到一次性事件中。