一、LiveData简介
LiveData是一个保存可以被观察的值的数据持有类。与普通的观察者不同,LiveData遵循应用组件的生命周期,比如Observer可以指定他应该观察的具体的Lifecycle。
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。
您可以注册与实现 LifecycleOwner 接口的对象配对的观察者。有了这种关系,当相应的 Lifecycle 对象的状态变为 DESTROYED 时,便可移除此观察者。 这对于 Activity 和 Fragment 特别有用,因为它们可以放心地观察 LiveData 对象而不必担心泄露(当 Activity 和 Fragment 的生命周期被销毁时,系统会立即退订它们)。
二、LiveData 特点
确保界面符合数据状态
LiveData 遵循观察者模式。当生命周期状态发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。观察者可以在每次发生更改时更新界面,而不是在每次应用数据发生更改时更新界面。
不会发生内存泄漏
观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
不会因 Activity 停止而导致崩溃
如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。
不再需要手动处理生命周期
界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
数据始终保持最新状态
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
适当的配置更改
如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
共享资源
您可以使用单一实例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。详情请参阅扩展 LiveData。
三、使用LiveData步骤
1、创建 LiveData 实例以存储某种类型的数据。这通常在 ViewModel 类中完成。
2、创建可定义 onChanged() 方法的 Observer 对象,该方法可以控制当 LiveData 对象存储的数据更改时会发生什么。通常情况下,您可以在界面控制器(如 Activity 或 Fragment)中创建 Observer 对象。
3、使用 observe() 方法将 Observer 对象附加到 LiveData 对象。observe() 方法会采用 LifecycleOwner 对象。这样会使 Observer 对象订阅 LiveData 对象,以使其收到有关更改的通知。通常情况下,您可以在界面控制器(如 Activity 或 Fragment)中附加 Observer 对象。
当您更新存储在 LiveData 对象中的值时,它会触发所有已注册的观察者(只要附加的 LifecycleOwner 处于活跃状态)。LiveData 允许界面控制器观察者订阅更新。当 LiveData 对象存储的数据发生更改时,界面会自动更新以做出响应。
A、添加LiveData 依赖:
// 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"
B、创建 LiveData 对象,LiveData 是一种可用于任何数据的封装容器,其中包括可实现 Collections 的对象,如 List。LiveData 对象通常存储在 ViewModel 对象中:
class ViewModelLiveData:ViewModel() {
var mNameEvent: MutableLiveData<String> = MutableLiveData()
fun getNameEvent(): MutableLiveData<String> {
return mNameEvent
}
}
C、接着,我们在Activity中创建ViewModel 的对象,并通过该对象监听ViewModel 里面mNameEvent的数据变化,当数据改变的时候,通过textView显示在界面上。这样我们就完成了对mNameEvent数据源的观察。
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="我们通过TextView显示来观察值的变化:"
android:textSize="18sp"
android:paddingStart="12dp"
android:textColor="@color/black"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/tvObserver"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvTitle"
android:paddingVertical="15dp"
android:paddingStart="12dp"
android:textColor="@color/black"
android:textSize="18sp"/>
D、然后在onCreate()方法里面:
var mTestViewModel = ViewModelProvider(this).get(ViewModelAct::class.java)
var nameEvent = mTestViewModel.getNameEvent()
nameEvent.observe(this, Observer<String> {
println("接收到的值it====$it")
tvObserver.text = it
})
tvTitle.setOnClickListener {
var random = System.currentTimeMillis()
mTestViewModel.getNameEvent().value = "Stella $random"
}
我们再来看另一个例子:
先是三个变量:
Gson gson = new Gson();
MutableLiveData<List<Person>> personLiveData = new MutableLiveData<>();
List<Person> personList = new ArrayList<>();
然后是调用MutableLiveData的observe方法对personLiveData的变化进行监听:
private void observe() {
personLiveData.observe(this, new Observer<List<Person>>() {
@Override
public void onChanged(List<Person> people) {
String mPeople = gson.toJson(people);
tvData.setText(mPeople);
}
});
}
接着是我们模拟网络请求,让数据发生改变:
btnDefaultAddData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for (int i = 1; i < 6; i++) {
Person person = new Person("默认的:" + (i * i), i);
personList.add(person);
}
personLiveData.postValue(personList);
}
});
btnAddData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for (int i = 20; i < 25; i++) {
Person person = new Person("默认的:" + (i * i), i);
personList.add(person);
}
List<Person> personList00 = personLiveData.getValue();
personList00.addAll(personList);
personLiveData.postValue(personList00);
}
});
使用LiveData时需要注意以下几点:
1、LiveData是一个抽象类,我们不能够直接使用,需要使用他的子类也就是具体实现类,比如我们这里用到的MutableLiveData。
2、LiveData的post/set方法都是protected关键字修饰的,是受保护的方法,只能是在LiveData所在包下的模块能够通过LiveData的对象访问,其他地方是无法访问的:
/**
* Posts a task to a main thread to set the given value. So if you have a following code
* executed in the main thread:
* <pre class="prettyprint">
* liveData.postValue("a");
* liveData.setValue("b");
* </pre>
* The value "b" would be set at first and later the main thread would override it with
* the value "a".
* <p>
* If you called this method multiple times before a main thread executed a posted task, only
* the last value would be dispatched.
*
* @param value The new value
*/
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
/**
* Sets the value. If there are active observers, the value will be dispatched to them.
* <p>
* This method must be called from the main thread. If you need set a value from a background
* thread, you can use {@link #postValue(Object)}
*
* @param value The new value
*/
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
3、因为MutableLiveData继承自LiveData,然后将post/set方法的访问权限由protected 改为public,因此我们可以通过MutableLiveData对象访问post/set方法,但是MutableLiveData里面只做了两件事情:一是将父类LiveData的post/set方法的访问权限由protected 改为public;二是调用父类的postValue(value)/setValue(value)。最主要的是第一点。:
public class MutableLiveData<T> extends LiveData<T> {
@Override
public void postValue(T value) {
super.postValue(value);
}
@Override
public void setValue(T value) {
super.setValue(value);
}
}
网上有人这么说:
LiveData可以实现数据的局部更新,MutableLiveData是整体更新。比如实体类只改一个字段,对应的LiveData那就是UI只更新这个字段相关的控件。MutableLiveData是整个类的数据有关的UI都更新了。
我们通过查看MutableLiveData.java的源码就只这种说纯粹是瞎扯淡的。也不怕扯得蛋疼哦!!
4、从上面第2点对post/set方法的注释我们可以知道:post方法主要用于子线程往主线程发送数据,实现跨线程通信的无缝切换。set方法则是只能用于主线程的通信。
如果我们在不同的地方同时调用了post/set方法,比如下面:
personLiveData.postValue("a");
personLiveData.setValue("b");
那么系统会先设置b值,然后在主线程里面用a值覆盖b值。这是因为setValue方法是在主线程运行,不存在跨线程通信,速度很快。
当系统在主线程里面先设置b值后,这时子线程里面通过postValue方法发送的a值也到达了。这时候系统就会用b值覆盖掉刚刚设置的a值。
最后:如果我们多次调用postValue方法,那么生效的只有最后一次传值操作。
四、Fragment Activity 之间共享数据
我们回过头来再来看一下 ViewModelProvider 的 of 方法,他主要有四个重载方法,分别是:
ViewModelProvider of(@NonNull Fragment fragment)
ViewModelProvider of(@NonNull FragmentActivity activity)
ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory)
ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory)
数据共享原理分析:
前面两个 方法之间的主要区别是传入 Fragment 或者 FragmentActivity。而我们知道,通过 ViewModel of 方法创建的 ViewModel 实例, 对于同一个 fragment 或者 fragmentActivity 实例,ViewModel 实例是相同的,因而我们可以利用该特点,在 Fragment 中创建 ViewModel 的时候,传入的是 Fragment 所依附的 Activity。因而他们的 ViewModel 实例是相同的,从而可以做到共享数据。
在Activity里面:
var mkey = "mkey"
var mViewModelAct = ViewModelProvider(this,ViewModelLiveDataFactory(mkey)).get(ViewModelLiveData::class.java)
val nameEventAct = mViewModelAct.getNameEvent()
nameEventAct.observe(this, Observer<String> { tvObserver.text = s })
tvTitle.setOnClickListener {
var random = System.currentTimeMillis()
mViewModelAct.getNameEvent().value = "Stella $random"
}
在Fragment里面:
var mActivity = this;
var mViewModel = ViewModelProvider(mActivity, ViewModelLiveDataFactory(mkey)).get(ViewModelLiveData::class.java)
mViewModel.getNameEvent().observe(this, Observer<String> {
println("接收到的值it====$it")
tvObserver.text = it
var result: Boolean = (mViewModel === mViewModelAct)
})
这样就可以实现Fragment与Activity 之间的数据共享