LiveData介绍
LiveData也是Google I/O 大会上发布的架构组件, LiveData 是一个可以被观察的数据持有类,它可以感知 Activity、Fragment或Service 等组件的生命周期。
- LiveData是一个可被观察的数据持有者类,它可以通过添加观察者被其他组件观察其变更。
- 不过它和其他的可观察对象不同,它会与生命周期相关联,比如Activity的生命周期,LiveData能确保仅在Activity处于活动状态下才会更新。也就是说当观察者处于活动状态,才会去通知数据更新。
- 如在Activity中如果数据更新了但Activity已经是destroy状态,LivaeData就不会通知Activity(observer)。当然。LiveData的优点还有很多,如不会造成内存泄漏等。
- LiveData通常会配合ViewModel来使用,ViewModel负责触发数据的更新,更新会通知到LiveData,然后LiveData再通知活跃状态的观察者。
- LiveData可以避免内存泄漏,这个作用可以说是很实用了,因为要想避免内存泄漏,必须要感知到生命周期,而原本并没有提供额外的方法,像Glide采用了一个透明的Fragment来感知Activity的生命周期,这虽然是一个可行的方法,但总感觉并不是一个最优的方法。
简单地说,LiveData是一个数据持有类。它具有以下特点:
- 数据可以被观察者订阅;
- 能够感知组件(Fragment、Activity、Service)的生命周期;
- 只有在组件出于激活状态(STARTED、RESUMED)才会通知观察者有数据更新;
为什么要引进 LiveData
一,保证数据与界面的实时更新
LiveData采用了观察者模式设计,其中LiveData是被观察者,当数据发生变化时会通知观察者进行数据更新。通过这点,可以确保数据和界面的实时性。
二,有效避免内存泄漏
这是因为LiveData能够感知到组件的生命周期,它可以做到在组件处于激活状态的时候才会回调相应的方法,从而刷新相应的 UI,当组件状态处于destoryed状态时,观察者对象会被remove。
比如Activity的生命周期,LiveData能确保仅在Activity处于活动状态下(生命周期处于onStart与onResume时)才会更新,也就是说当观察者处于活动状态,才会去通知数据更新,当生命周期处于onStop或者onPause时,不回调数据更新,直至生命周期为onResume时,立即回调。
三,Activity/Fragment销毁掉时不会引起崩溃
这是因为组件处于非激活状态时,在界面不会收到来自LiveData的数据变化通知。这样规避了很多因为页面销毁之后,修改UI导致的crash。
四,不需要手动处理生命周期
LiveData能够感知组件的生命周期,所以就完全不需要在代码中告诉LiveData组件的生命周期状态。
五,始终能够保持最新数据
生命周期从非活跃状态切换到活跃状态的时候,能够实时的接收最新的数据。
组件在前台的时候能够实时收到数据改变的通知,这是可以理解的。当组件从后台到前台来时,LiveData能够将最新的数据通知组件,这两点就保证了组件中和数据相关的内容能够实时更新。
六,能够应对配置更改
我们知道,当你把数据存储在组件中时,当configuration change(比如语言、屏幕方向变化)时,组件会被recreate,然而系统并不能保证你的数据能够被恢复的。当我们采用LiveData保存数据时,因为数据和组件分离了。当组件被recreate,数据还是存在LiveData中,并不会被销毁。
由于LiveData保存数据的时候,组件和数据是分离的,所以在配置更改(如横竖屏切换等)的时候,即便组件被重新创建,因为数据还保存在LiveData中,这样也能够做到实时的更新。比如 config 导致activity 重新创建的时候,不需要手动取处理数据的储存和恢复,它已经帮我们封装好了。
七,资源共享
通过继承LiveData类,然后将该类定义成单例模式,在该类封装监听一些系统属性变化,然后通知LiveData的观察者。
单例模式扩展LiveData对象并包装成系统服务,以便在应用程序中进行共享,需要该资源的只需要观察LiveData即可。
LveData在MVVM中的角色
2018年 google的AAC(Android Architecture Components)。一套组合的Jetpack组件库,使得ViewModel具有生命周期感知能力,同时也有了数据的感知能力(LiveData)
理解LiveData图
不同于rxjava的观察模式,这里仅通知处于active状态的观察者。
一旦观察者回复Resume状态,就会收到最新的数据(有利有弊,特殊场景)。
LiveData的使用
Android开发中 MVX的开发架构设计,生命周期的感知对于Controller/Presenter/ViewModel不是天然可知。
回想一下,在你的项目中,是不是经常会碰到这样的问题,当网络请求结果回来的时候,你经常需要判断 Activity 或者 Fragment 是否已经 Destroy, 如果不是 destroy,才更新 UI。而当你如果使用 Livedata 的话,因为它是在 Activity 处于 onStart 或者 onResume 的状态时,他才会进行相应的回调,因而可以很好得处理这个问题,不必谢一大堆的 activity.isDestroyed()。接下来,让我们一起来看一下 LiveData 的使用。
LiveData几种使用方式
- 直接使用LiveData对象
- 继承LiveData类
LiveData依赖
目前Jetpack组件已经包含在AndroidX中,AndroidX是Jetpack的包含的支持库的具体位置,所以如果项目以及迁移到了AndroidX了,就不需要额外导包。如果没有兼容AndroidX 那么需要在gradle中进行配置。
ViewModel 的依赖,可以查看官方文档,导入最新的版本。
java
def lifecycle_version = "2.2.0"
def arch_version = "2.1.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
kotlin
def lifecycle_version = "2.2.0"
def arch_version = "2.1.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
LiveData使用方式1:直接使用LiveData对象
通常使用LiveData有4个步骤:
- 创建LiveData实例来保存数据,常常是配合ViewModel一起工作;
- 定义一个Observer的观察者对象,如果有数据更新会通过观察者的onChanged()方法来同步到UI上面;
- 将观察者Observer通过observe()方法进行绑定。
- 更新Livedata对象存储的数据;
LiveData 是一个抽象类,它的实现子类有 MutableLiveData ,MediatorLiveData。在实际使用中,用得比较多的是 MutableLiveData。他常常结合 ViewModel 一起使用。
创建LiveData实例
Android文档中建议LiveData配合ViewModel使用更加哦,其实呢,你也可以不使用ViewModel,但是一定要做到LiveData中保存的数据和组件分离。
首先,我们先写一个类继承我们的 ViewModel:
class MainViewModel() : ViewModel() {
private var liveData = MutableLiveData<String>()
var i = 0
fun getLiveData(): MutableLiveData<String> {
return liveData
}
}
创建Observer对象,作为参数传入LiveData.observe()方法添加观察者
我们在 Activity 中创建 ViewModel,并监听 ViewModel 里面 数据的变化,当数据改变的时候,我们 设置给 textView,显示在界面上。这样我们就完成了对 数据源的观察。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
//过时 2.2.0-alpha02 以前的版本是通过下面的方式,新版本已弃用。
//val mainViewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
val mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
//创建一个观察者来观察数据的变化
val observer: Observer<String> = object : Observer<String> {
override fun onChanged(t: String?) {
binding.textView.text = t
}
}
//订阅
mainViewModel.getLiveData().observe(this, observer)
//点击按钮更新数据
binding.btn.setOnClickListener {
mainViewModel.getLiveData().value = "hello ${mainViewModel.i++}"
}
}
}
最后当我们数据源改变的时候,我们需要调用 livedata 的 setValue 或者 postvalue 方法。他们之间的区别是, 调用 setValue 方法,Observer 的 onChanged 方法会在调用 serValue 方法的线程回调,而postvalue 方法,Observer 的 onChanged 方法将会在主线程回调。
LiveData有2个方法通知数据改变
setValue(value):同步,接收端数据回调与发送端同一个线程
postValue(value):异步,接收端在主线程回调数据
注意:
获取mainViewModel方式,代码中只有一个this参数,需要加入依赖lifecycle-extensions。
val mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
LiveData使用方式2:自定义类继承LiveData类
除了直接使用LiveDatad对象外,我们还可以通过集成LiveData类来定义适合特定需求的LiveData。
Livedata 主要有几个方法
void observe (LifecycleOwner owner, Observer observer)
在给定所有者的生命周期内将给定观察者添加到观察者列表中
void onActive ()
此方法是当处于激活状态的observer个数从0到1时,该方法会被调用。
当 LiveData 对象具有活跃观察者时,会调用 onActive() 方法。这意味着,您需要从此方法开始观察数据更新。
void onInactive ()
此方法是当处于激活状态的observer个数从1变为0时,该方法会被调用。
当 LiveData 对象没有任何活跃观察者时,会调用 onInactive() 方法。
observeForever:
- 跟 observe 方法不太一样的是,它在 Activity 处于 onPause ,onStop, onDestroy的时候,都可以回调 obsever 的 onChange 方法,但是有一点需要注意的是,我们必须手动 remove obsever,否则会发生内存泄漏。
- 当 Actiivty 不是处于激活状态的时候,如果你想 livedata setValue 之后立即回调 obsever的onChange 方法,而不是等到 Activity 处于激活状态的时候才回调 obsever 的 onChange 方法,你可以使用observeForever 方法,但是你必须在 onDestroy 的时候 remove Observer。
自定义 Livedata观察网络状态变化
首先我们自定义一个 Class NetworkLiveData,继承 LiveData,重写它的 onActive 方法和 onInactive 方法
在 onActive 方法中,我们注册监听网络变化的广播,即ConnectivityManager.CONNECTIVITY_ACTION。在 onInactive 方法的时候,我们注销广播。
public class NetworkLiveData extends LiveData<NetworkInfo> {
private final Context mContext;
static NetworkLiveData mNetworkLiveData;
private NetworkReceiver mNetworkReceiver;
private final IntentFilter mIntentFilter;
private static final String TAG = "NetworkLiveData";
public NetworkLiveData(Context context) {
mContext = context.getApplicationContext();
mNetworkReceiver = new NetworkReceiver();
mIntentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
}
public static NetworkLiveData getInstance(Context context) {
if (mNetworkLiveData == null) {
mNetworkLiveData = new NetworkLiveData(context);
}
return mNetworkLiveData;
}
@Override
protected void onActive() {
super.onActive();
Log.d(TAG, "onActive:");
mContext.registerReceiver(mNetworkReceiver, mIntentFilter);
}
@Override
protected void onInactive() {
super.onInactive();
Log.d(TAG, "onInactive: ");
mContext.unregisterReceiver(mNetworkReceiver);
}
private static class NetworkReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager manager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
getInstance(context).setValue(activeNetwork);
}
}
}
这样,当我们想监听网络变化的时候,我们只需要调用相应的 observe 方法即可,方便又快捷。
NetworkLiveData.getInstance(this).observe(this, new Observer<NetworkInfo>() {
@Override
public void onChanged(@Nullable NetworkInfo networkInfo) {
Log.d(TAG, "onChanged: networkInfo=" +networkInfo);
}
});
构造器有参数的ViewModel
我们的 ViewModel 是通过ViewModelProvider(this).get(MainViewModel::class.java)方法创建出来的,如果我们要携带参数,怎么办?
当我们的ViewModel需要进行构造器需要穿参数的时候,就不能像上面一样进行实例化了。而需要借助于ViewModelProvider的Fatory来进行构造。
class MainViewModel(val name: String) : ViewModel() {
private val mainLiveData = MutableLiveData<String>()
fun loadData() {
mainLiveData.postValue("xyh$name")
}
fun getMainLiveData(): MutableLiveData<String> {
return mainLiveData
}
class MainViewModelFactory(private val name: String) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MainViewModel(name) as T
}
}
}
可以看到在MainViewModel有一个内部类基础自ViewModelProvider.Factory接口。并重写了create方法。这个方法返回一个ViewModel这里我们就是要实例化MainViewModel对象,因此这里返回一个MainViewModel对象,可以看到参数这时候就通过MainViewModelFactory传给了MainViewModel了。
这时候不光是MainViewModel要进行修改,在进行MainViewModel的获取的时候也是需要进行相应的修改的:
val mainViewModel = ViewModelProvider(
this,
MainViewModel.MainViewModelFactory("赵丽颖")
).get(MainViewModel::class.java)
Factory 是一个接口,它只有一个 create 方法:
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
* <p>
*
* @param modelClass a {@code Class} whose instance is requested
* @param <T> The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
在实际当中,我们的做法是:实现 Factory 接口,重写 create 方法,在create 方法里面调用相应的构造函数,返回相应的实例。
Fragment和 Activity之间之间共享数据
Fragment和 Activity 之间共享数据
Fragment 和所依附的 Activity,因而他们的 ViewModel 实例是相同的,从而可以做到共享数据。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
supportFragmentManager.beginTransaction().replace(R.id.flContent,MyFragment()).commit()
//Activity传递数据到ViewModel
val mainViewModel = ViewModelProvider(this,
MainViewModel.MainViewModelFactory("赵丽颖")).get(MainViewModel::class.java)
mainViewModel.loadData()
mainViewModel.getMainLiveData().observe(this, {
Log.e("xyh", "MainActivity: $it")
})
}
}
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val mainViewModel = activity?.let { ViewModelProvider(it).get(MainViewModel::class.java) }
mainViewModel?.loadData()
mainViewModel?.getMainLiveData()?.observe(this, {
Log.e("xyh", "MyFragment:$it")
//获取activity的共享数据
Log.e("xyh", "MyFragment name=${mainViewModel.getName()} ")
})
}
}
class MainViewModel(private val name: String) : ViewModel() {
private val mainLiveData = MutableLiveData<String>()
fun loadData() {
mainLiveData.postValue("zly")
}
class MainViewModelFactory(private val name: String) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MainViewModel(name) as T
}
}
fun getMainLiveData(): MutableLiveData<String> {
return mainLiveData
}
fun getName(): String {
return name
}
}
这样,MainActivity和 Fragment中的 ViewModel 是同一个实例。即 Activity 和 Fragment 共享数据。
Fragment和Fragment之间共享数据
一个Activity中的多个Fragment相互通讯是很常见的。之前每个Fragment需要定义接口描述,所属Activity将二者捆绑在一起。此外,每个Fragment必须处理其他Fragment未创建或不可见的情况。通过使用ViewModel可以解决这个痛点,这些Fragment可以使用它们的Activity共享ViewModel来处理通讯。
class ShareViewModel:ViewModel() {
private val mutableLiveData = MutableLiveData<String>()
fun setData(value:String) {
mutableLiveData.value = value
}
fun getData(): LiveData<String> {
return mutableLiveData
}
}
class Fragment1 : Fragment() {
private lateinit var mBinding: Fragment1Binding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mBinding = DataBindingUtil.inflate<Fragment1Binding>(
inflater,
R.layout.fragment1,
container,
false
)
return mBinding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val viewModel = ViewModelProvider(activity!!).get(ShareViewModel::class.java)
mBinding.btn.setOnClickListener {
viewModel.setData("xyh")
}
}
}
class Fragment2 : Fragment() {
private lateinit var mBinding: Fragment2Binding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mBinding = DataBindingUtil.inflate<Fragment2Binding>(
inflater,
R.layout.fragment2,
container,
false
)
return mBinding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val viewModel = ViewModelProvider(activity!!).get(ShareViewModel::class.java)
viewModel.getData().observe(viewLifecycleOwner, Observer {
mBinding.tv.text = it
})
}
}
注意:上面两个Fragment都用到了如下代码来获取ViewModel,activity返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个SharedViewModel对象。
val viewModel = ViewModelProvider(activity!!).get(ShareViewModel::class.java)
这种方式的好处包括:
- Activity不需要做任何事情,也不需要知道通讯的事情
- Fragment不需要知道彼此,除了SharedViewModel进行联系。如果它们(Fragment)其中一个消失了,其余的仍然能够像往常一样工作。
- 每个Fragment有自己的生命周期,而且不会受其它Fragment生命周期的影响。事实上,一个Fragment替换另一个Fragment,UI的工作也不会受到任何影响。
全局共享数据
说到全局共享数据,我们想一下我们的应用全景,比如说我的账户数据,这个对于整个 App 来说,肯定是全局共享的。有时候,当我们的数据变化的时候,我们需要通知我们相应的界面,刷新 UI。如果用传统的方式来实现,那么我们一般才采取观察者的方式来实现,这样,当我们需要观察数据的时候,我们需要添加 observer,在界面销毁的时候,我们需要移除 observer。
但是,如果我们用 LiveData 来实现的话,它内部逻辑都帮我们封装好了,我们只需要保证 AccountLiveData 是单例的就ok,在需要观察的地方调用 observer 方法即可。也不需要手动移除 observer,不会发生内存泄漏,方便快捷。
这里 AccountLiveData 的实现就不贴出来了,可以参考上面的 NetworkLiveData 实现。
MediatorLiveData
MutableLiveData的子类,中介者,媒介,将多个liveData的数据,合并处理成一个LiveData。
mediator的liveData可以监听A,B两个数据源的变化,通过addSource后,并响应A/B的变化,转化为mediator的变化。
MediatorLiveData是一个LiveData的子类,它将活跃状态或者非活跃状态传播到源LiveData上;也就是说它相当于是一个中间商,通过addSource进行注册的LiveData,当数据进行更新时通过中间商倒一手再进行处理。
MediatorLiveData 可以接管普通的 LiveData,使得当 LiveData 有数据更新的时候,MediatorLiveData 也能够 “收到响应”。
我们看一下官方介绍的两个场景:
- 有多个LiveData,我们想要同时监听这两个数据源,只要他们之中有一个数据源更新则接收到通知
我们看一下具体需要怎么做:
class MainViewModel:ViewModel() {
//数据源1
val liveData1=MutableLiveData<String>().apply {
value="zly1"
}
//数据源2
val liveData2=MutableLiveData<String>().apply {
value="zly2"
}
val mediatorLiveData=MediatorLiveData<String>()
init {
//为mediator addSource,监听其他的liveData
mediatorLiveData.addSource(liveData1, Observer {
mediatorLiveData.value=it
})
mediatorLiveData.addSource(liveData2, Observer {
mediatorLiveData.value=it
})
}
}
//通过改变数据源1或者2的数据,接收到更新
viewModel.mediatorLiveData.observe(this, Observer {
Log.e("xyh", "onCreate: $it")
})
- 如果数据源变化频繁,我们想要只检测前10个数据变化,之后取消数据观察,我们可以用中间商这么做:
liveDataMerger.addSource(liveData1, new Observer() {
private int count = 1;
@Override public void onChanged(@Nullable Integer s) {
count++;
//1.当数据源1更新后,通知中间商进行更新
liveDataMerger.setValue(s);
//2.满足条件后,我们进行取消
if (count > 10) {
liveDataMerger.removeSource(liveData1);
}
}
});
如果inactive()下,A,B都变化,则resume后,也只接受最新的变化。
MediatorLiveData 应用场景
假设我们需要获取数据,然后将数据的内容展示到UI上,我们有两种方式获取数据,第一种就是拿本地的数据,第二种就是拿网络的数据。我们可以定义两个 MutableLiveData ,对应两种不同方式获取的数据,因为是同一种数据类型,所以我们可以通过 MediatorLiveData 使用 addSource() 来接管两个 MutableLiveData ,并且传入同一个 Observer,这样我们就只需要在 Observer 中进行操作即可……(细思其实并没什么卵用)
Transformations
Transformations 类是 LiveData 的一个功能扩展类,其是一个私有化构造方法的工具类,且只提供 3 个方法使用,虽然数量不多,但胜在实用,这三个方法分别是:
- map
- switchMap
- distinctUntilChanged
map
类似于 RxJava 的 map 操作符、Kotlin 中数组的扩展函数 map,其实 Transformations 的 map 方法也是一个转化的功能。
class MainViewModel : ViewModel() {
val liveData = MutableLiveData<String>().apply {
value = "zly"
}
// 通过 Transformations.map 返回一个 LiveData
val mapLiveData = Transformations.map(liveData, Function<String, String> {
it.plus(" xyh")
})
}
viewModel.mapLiveData.observe(this, Observer {
Log.e("xyh", "onCreate: $it")
})
控制台输出:
onCreate: zly xyh
map() 方法的第二个参数的类型有两个泛型,事实上第一个个泛型的类型是确定的,那就是对应 map() 方法第一个参数 LiveData 的泛型,那么第二个泛型就是控制返回的 LiveData 的类型。改造上面的例子:
class MainViewModel : ViewModel() {
val liveData = MutableLiveData<String>().apply {
value = "zly"
}
// 通过 Transformations.map 返回一个 LiveData
val mapLiveData = Transformations.map(liveData, Function<String, Int> {
it.length
})
}
控制台输出:
onCreate: 3
Kotlin 扩展写法:
val mapLiveData =liveData.map { it.plus("xyh") }
switchMap
看名字,是不是又联想到了 RxJava 的 switchMap 操作符?
实际上 Transformations 的 switchMap 也是一样的功能:舍弃原来的 LiveData,创建一个新的 LiveData 来接管它的变化。
class MainViewModel : ViewModel() {
val liveData = MutableLiveData<String>().apply {
value = "zly"
}
// 通过 Transformations.switchMap返回一个 LiveData
val switchMapData = Transformations.switchMap(liveData, Function {
return@Function MutableLiveData<String>().apply {
this.value = it.plus("xyh")
}
})
}
控制台输出:
zly xyh
对比一下 map() 方法,switchMap() 显得如此的不堪……但存在即合理,它的也有它的使用场景。
我们先对比一下 switchMap() 和 map() 的使用过程:map() 的操作已经是在消费上层 LiveData 的值,而 switchMap() 同样使消费了上层 LiveData 的值,但是它又创建了新的生产者,所以其真实的消费并不是由 switchMap() 来执行的。
知道了这点后,我们就可以比较清晰的了解 switchMap() 的使用场景了:那就是通过其创建一个新的 LiveData,并且我们可以在其间做一些操作,无论是单纯的转变类型,或是时间上的耗时操作……
Kotlin 扩展:
val switchMapData = liveData.switchMap {
return@switchMap MutableLiveData<String>().apply {
this.value = it.plus("world")
}
}
distinctUntilChanged
这个方法也能够返回一个监听 source 的 LiveData,当源 LiveData 发生变化 ,其返回的 LiveData 也能够发生变化……这可不就是map()吗?但不同的是,如果源 LiveData 一直进行 setValue()/postValue() 同一个值,那么返回的 LiveData 只接收第一次返回的值,除非源 LiveData 设置新的值。
private val originData = MutableLiveData<String>()
// 通过 Transformations.distinctUntilChanged 返回一个 LiveData
val newData = Transformations.distinctUntilChanged(originData)
newData.observe(this, Observer {
Log.e("newData",it)
})
// 点击按钮给 originData 赋值
btn.setOnClickListener {
originData.value = "Hello"
}
然后疯狂的点击按钮,而控制台只有一行输出,并且通过源码我们也能够发现它没有转换类型的功能,source 的类型是怎样的,那么返回的 LiveData 的类型就是怎样的。
Kotlin 扩展:
val newData = originData.distinctUntilChanged()
Transformations 的 3 个函数都十分的具有实用性,在具体的开发过程中,我们在业务层完全就可以依赖 LiveData 去实现值的传递的过程,甚至是变换的过程。使用起来也具有较强的灵活性、技巧性。
LiveData使用问题
Android开发 SingleLiveEvent解决LiveData或者MutableLiveData多次回调的问题
只要使用过一段时间的LiveData就会发现,LiveData会经常多次回调数据。我们经常碰到的这个问题。
场景1:
使用LiveData作为事件Event不合适:
通过liveData的Event跳转详情页,手动点击触发 observe中true则跳转,返回首页,则liveData重新active,则再次跳转。(不应尝试多次变更,不建议愚蠢的手动reset)
场景2:
我们的ViewModel里是给Activity持有的并且里面有一个LiveData数据,我们A_Fragment现在获得Activity的ViewModel并且注册LiveData数据成为观察者,这个时候我们setValue()就会让前台的A_Fragment得到一次LiveData数据,接下来操作 A_Fragment 启动 B_Fragment,在返回到A_Fragment。 你会发现只要再次注册LiveData的observe(this, new Observer …),那么A_Fragment里面又会接收到一次LiveData的数据。
为什么会这样呢?
-
一部分原因是LiveData的机制,就是向所有前台Fragment或者Activity发送数据。只要注册的观察者在前台就必定会收到这个数据。
-
另一部分的原因是对ViewModel理解不深刻,理论上只有在Activity保存的ViewModel它没被销毁过就会一直给新的前台Fragment观察者发送数据。我们需要管理好ViewModel的使用范围。 比如只需要在Fragment里使用的ViewModel就不要给Activity保管。而根Activity的ViewModel只需要做一下数据共享与看情况使用LiveData。
针对liveData用作event的时候,由于即使在inactive状态下,不论有无observer都能set值,并被持有,恢复active即被通知observer,在特定场景下,不适用。(如ViewModel中的liveData,在UI中感知,来跳转界面;就会出现返回界面后,会再次跳转的问题。
处理方案:是判断处理情况,再决定通知事件。
通俗的,低级的方式
// 通俗的,低级的方式
open class LowLiveEvent<out T>(private val content: T) {
private var hasHandled = false;//标记位
//如果没有处理,就返回content,否则null
fun getContentIfNotHandled(): T? {
return if (hasHandled) {
hasHandled = true
content
} else {
content
}
}
//不论已经处理与否,都感知变化
fun peekContent(): T = content
}
使用
//使用
val liveD = mutableLiveData<LowLiveEvent<String>>()
//
liveD.observe(this,Observer{
it.getContentIfnotHandled()?.let{
//do something
}
})
第二种方法:使用SingleLiveEvent
就是使用一个google大神实现的一个复写类 SingleLiveEvent,其中的机制是用一个原子 AtomicBoolean记录一次setValue。在发送一次后在将AtomicBoolean设置为false,阻止后续前台重新触发时的数据发送。
//SingleLiveEvent 原理类似于上面的,通过AtomicBoolean来标记对比处理情况,
//会选择通知一个处于active的observe变化,但是不确保是哪一个
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Log.w(
"SingleLiveEvent",
"Multiple observers registered but only one will be notified of changes."
)
}
// Observe the internal MutableLiveData
super.observe(owner, Observer { t ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
@MainThread
override fun setValue(t: T?) {
mPending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
}
LiveData原理和源码分析
原理
观察者模式+ Lifecycle(LiveData 的生命周期感知是由Lifecycle来实现的)。
LiveData既是被观察者,可以被其他观察者观察数据的变化。
LiveData也是观察者,可以观察Activity的生命周期的变化。
LiveData是被观察者,Activity中创建观察者,订阅被观察者LiveData后,被观察者LiveData数据的变化时会通知观察者,当Activity处于活动状态,才会去通知数据更新,通过Lifecycle来监听Activity是否处于活动状态。
源码分析
LiveData
// 注意,他是 abstract class
public abstract class LiveData<T> {
...省略其他代码...
// 只有 onStart 后,对数据的修改才会触发 observer.onChanged()
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {}
// 无论何时,只要数据发生改变,就会触发 observer.onChanged()
public void observeForever(@NonNull Observer<T> observer) {}
...省略其他代码...
}
MutableLiveData
由于 LiveData 是一个 abstract class,我们不能直接生成他的实例。官方有提供他的实现类MutableLiveData。
MutableLiveData实际上就是继承自LiveData,没有什么特别:
public class MutableLiveData<T> extends LiveData<T> {
/**
* Creates a MutableLiveData initialized with the given {@code value}.
*
* @param value initial value
*/
public MutableLiveData(T value) {
super(value);
}
/**
* Creates a MutableLiveData with no value assigned to it.
*/
public MutableLiveData() {
super();
}
@Override
public void postValue(T value) {
super.postValue(value); //postValue内部实际上调用的也是setValue
}
@Override
public void setValue(T value) {
super.setValue(value);
}
}
我们根据在MainActivity 使用LiveData来分析源码
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
//过时 2.2.0-alpha02 以前的版本是通过下面的方式,新版本已弃用。
//val mainViewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
val mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
//创建一个观察者来观察数据的变化
val observer: Observer<String> = object : Observer<String> {
override fun onChanged(t: String?) {
binding.textView.text = t
}
}
//订阅一个观察者
mainViewModel.getLiveData().observe(this, observer)
//点击按钮更新数据
binding.btn.setOnClickListener {
mainViewModel.getLiveData().value = "hello ${mainViewModel.i++}"
}
}
}
创建了Livedata 后,需要通过observe方法或者observeForever 方法设置一个回调,这个回调接口就是Observer:
public interface Observer<T> {
/**
* Called when the data is changed.
* @param t The new data
*/
void onChanged(T t);
}
一. LiveData的observer方法
observeForever 的实现跟 observe 是类似的,这里我们重点看一下 observe()的实现过程。
- viewModel.getLiveData()获取到 我们的 LiveData,然后调用LiveData的observer方法,并把当前MainActivity 作为参数传递进去。
- observer() 方法就是我们分析的入口了,接下来我们看LiveData的observer()方法都做了什么:
public abstract class LiveData<T> {
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe"); //判断是否是主线程
//标注1
//LifecycleOwne(MainActivity )当前的状态
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//标注2
//LifecycleBoundObserver包装了owner和observer,可以感知Activity的生命周期
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;
}
//添加观察者
//wrapper就可以监听Activity/Fragment的生命周期
//当UI的生命周期发生变化的时候,就会去回调wrapper中的 onStateChanged
owner.getLifecycle().addObserver(wrapper);
}
}
- 可以看到,当前Activity是作为 LifeCycleOwner 参数传进来的,Activity会默认继承LifecycleOwner,而 LifecycleOwner 可获取到该组件的LifeCycle,也就知道了Activity组件的生命周期的状态。
- 看标注1处,如果我们的 Activity组件已经是destroy状态的话,将直接返回,不会被加入观察者行列。
- 如果不是destroy状态,就到标注2处,新建一个 LifecycleBoundObserver 将我们的 LifecycleOwner 和 observer保存起来,然后调用 mObservers.putIfAbsent(observer, wrapper) 将observer和wrapper分别作为key和value存入Map中,putIfAbsent()方法会判断如果 value 已经能够存在,就返回,否则返回null。
- 如果返回existing为null,说明以前没有添加过这个观察者,就将 LifecycleBoundObserver 作为 owner 生命周期的观察者,也就是作为 Activity生命周期的观察者。
二. LiveData作为观察者
LiveData观察Activity生命周期发生变化,LiveData回调接口的调用情况
LifecycleBoundObserver 源码:
- 我们来看一个当生命周期发生变化的时候,LifecycleBoundObserver都做了那些操作,熟悉LifeCycle的小伙伴,应该会很熟悉,生命周期发生变化时,会调用onStateChanged。
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
//STARTED RESUMED状态时为true
//表示Activity处于活动状态,可以更新数据
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
//Activity生命周期变化时回调到这里
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
// 当Activity的生命周期为DESTROYED,取消对数据变化的监听,移除Observer
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
//改变数据,传递的参数是shouldBeActive(),
//它会计算看当前的状态是否是STARTED,也就是 onStart-onPause 期间生命周期
activeStateChanged(shouldBeActive());
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
- 代码并不多,LifecycleBoundObserver 继承自 ObserverWrapper 并实现了 LifecycleEventObserver 接口,而 LifecycleEventObserver 接口又继承自 LifecycleObserver 接口,那么根据 Lifecycle 的特性,实现了LifecycleObserver接口并且加入 LifecycleOwner 的观察者里就可以感知或主动获取 LifecycleOwner 的状态。
我们继续看下activeStateChanged方法是如何对数据进行处理的,它是ObserverWrapper 类中的一个方法:
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
//Activity是否处于活动状态
//Activity生命周期状态为 STARTED RESUMED状态时为true
boolean mActive;
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
abstract boolean shouldBeActive();
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}
void detachObserver() {
}
void activeStateChanged(boolean newActive) {
// 当前的生命周期和上一次的生命周期状态,是否发生变化,没有发生变化,就直接返回。
// onStart-onPause 为 true 在这之外的生命周期为false
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
// 这是一个空函数,可在代码中根据需要进行重写
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
// 这是一个空函数,可在代码中根据需要进行重写
onInactive();
}
//Activity活跃状态
//结合上面的状态判断,我们知道了,生命周期状态从Inactive 到 Active, 就会调用回调函数
if (mActive) {
dispatchingValue(this);
}
}
}
- dispatchingValue
void dispatchingValue( 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
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
为什么在生命周期的活跃状态 从Inactive 到 Active,要去调用livedata设置的回调函数呢?
原因是,在Inactive (在非 onStart-onPause 周期内 )状态,数据发生了变化,然后在回到Active(onStart-onPause 周期内),如果不去调用回调函数,会出现UI的界面,还在显示上一次的数据,所以需要调用回调函数。
再回到代码流程,到了调用dispatchingValue(this);这个函数在数据发生变化,也是会去调用的,所以在后面分析,先来看下,数据发生变化,代码的流程
三. LiveData作为被观察者
LiveData数据发生发生变化,调用回调接口
好了,看完了观察者,那么我们的LiveData什么时候会通知观察者呢?不用想,肯定是数据更新的时候,而数据的更新是我们代码自己控制的。
//点击按钮更新数据
binding.btn.setOnClickListener {
mainViewModel.getLiveData().value = "hello ${mainViewModel.i++}"
}
LiveData的setValue方法:
private int mVersion;
@MainThread
protected void setValue(T value) {
assertMainThread("setValue"); //检查是否在主线程
mVersion++; //mVersion 表示数据发生了变化
mData = value; //将要更新的数据赋给mData
//调用 dispatchingValue()方法并传入null,将数据分发给各个观察者
dispatchingValue(null);
}
进入到dispatchingValue方法:
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
//标注1
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
//标注2
//遍历观察者集合
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
//通知观察者调用considerNotify()方法
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
- 从标注1可以看出,dispatchingValue()参数传null和不传null的区别就是如果传null将会通知所有的观察者,反之仅仅通知传入的观察者。
- 我们直接看标注2,通知所有的观察者通过遍历mObservers ,将所有的 ObserverWrapper 拿到,实际上就是我们上面提到的LifecycleBoundObserver,通知观察者调用considerNotify()方法,这个方法就是通知的具体实现了。
看下considerNotify 的函数,调用了之前livedata设置的observer的onChanged函数:
private void considerNotify(ObserverWrapper observer) {
//如果观察者不是活跃状态,将不会通知此观察者
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
// 如果当前的生命周期是非活跃,就不回调onChanged函数,
// 在LifecycleBoundObserver 中记录状态,当生命周期变为活跃,就回去更新数据
observer.activeStateChanged(false);
return;
}
//数据发生变化了
if (observer.mLastVersion >= mVersion) {
return;
}
//Activity活跃状态,就会走到这里
observer.mLastVersion = mVersion;
//调用LiveData的observer()方法传入的 Observer,然后调用 Observer 的 onChanged((T) mData)
//方法,将保存的数据mData传入,也就实现了更新。
observer.mObserver.onChanged((T) mData);
}
在看下我们实现的Observer:
//创建一个观察者来观察数据的变化
val observer: Observer<String> = object : Observer<String> {
override fun onChanged(t: String?) {
binding.textView.text = t
}
}
//订阅
mainViewModel.getLiveData().observe(this, observer)
如果哪个控件要根据数据的变更而及时更新,就在onChanged()方法里处理就可以了。到这里,LiveData已经能够分析完了,其实LiveData的实现还是要依赖于Lifecycle。
postValue 方法
看完了 setValue,postValue 就很简单了:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
// 上一个 post 后还没有执行的 runnable,所以就不需要再 post 了,
// 但是注意,上面的mPendingData 数据已经是新数据了
//用官方的话,就是 If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.
return;
}
// postValue 可以从后台线程调用,因为它会在主线程中执行任务
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
//noinspection unchecked
//这里调用了setValue,和上面分析的流程一样
setValue((T) newValue);
}
};
总结
- LiveData 内部已经实现了观察者模式,如果你的数据要同时通知几个界面,可以采取这种方式 。
- 我们知道 LiveData数据变化的时候,会回调 Observer 的 onChange 方法,但是回调的前提是 lifecycleOwner(即所依附的Activity 或者 Fragment) 处于 started 或者 resumed 状态,它才会回调,否则,必须等到 lifecycleOwne 切换到前台的时候,才回调。因此,这对性能方面确实是一个不小的提升。但是,对于你想做一些类似与在后台工作的(黑科技),liveData就不太适合了,你可以使用 observeForever 方法,或者自己实现观察者模式去吧。