在上篇讲了下ViewModel 这次接着讲LiveData
下一篇 Jetpack mvvm 三部曲(三) DataBinding
先放下本jetpak系列在学习过程写的demojetpackDemo
先贴下官方的链接LiveData
根据官方给的说发LiveData可为数据提供观察者
(就是一个接口)
,在数据进行改变的时候活跃的观察者可以获取到数据的变化非活跃的观察者是不能收到变化通知的-
我在这里写了个计时器先记录生命周期的状态以及LiveData数值,当数据产生变化打印看下生命周期方法以及当前的数值,可以看到在app回到桌面进入到onPause后观察者就收不到通知了,而重新从后台回到app进入onResume状态观察者又能收到通知了。
LiveData能做到这点还是要归功于LifecycleOwner接口,当然AndroidX的AppCompatActivity类已经帮我们实现了LifecycleOwner接口
先看下怎么实现在看下源码
引用(2.2.0目前是最新版本) 其余版本可参考
implementation 'androidx.lifecycle:lifecycle-livedata:2.2.0'
- 鉴于官方的提醒所以这次例子就是ViewModel+LiveData了,下面创建一个ViewModel,如果不了解可以看我上一篇文章Jetpack mvvm 三部曲(一) ViewModel
public class MyViewModel extends ViewModel {
public MutableLiveData<String> stringMediatorLiveData;
public MyViewModel() {
stringMediatorLiveData = new MutableLiveData<>();
}
}
- 实现监听LiveData数据的变化使用observe方法传入LifecycleOwner,在实现观察者Observer
接口
就行了,通过setValue 或者 postValue改变LiveData的值就能在Observer接口的onChanged方法收到改变后的数值了。 -
这里有一个注意的点在主线程使用可以用setValue,如果是子线程改变值用postValue,因为在多线程中操作数据是不安全的,而postValue是通过synchronized关键字确保线程安全
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.stringMediatorLiveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.e("MainActivity",s+"----");
}
});
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
viewModel.stringMediatorLiveData.setValue("哈哈哈哈");
}
});
-
实现的代码敲完了,接着看下源码。
图一是传LifecycleOwner过去
图二是传递保存在一个map对象中
图3是把LifecycleObserver拿到更新生命周期状态
图4是回调LiveDataon中LifecycleBoundObserver类的StateChanged方法见图5
图6调用dispatchingValue改变值的时候会通过mObservers这个map对象进行遍历见图7,从map集合中取出观察者
ObserverWrapper接口
调用onChanged方法进行回调通知注意图8的第一个红框return
如果observer.mActiveactivity的活跃状态
为false就直接不走下面的方法了,这个mAtive参数的赋值见图5 activeStateChanged(shouldBeActive()); shouldBeActive这个方法见图9
-------------------------------分割线-------------------------------
- 到这里LiveData你就有了初步的认知,在上面的例子是直接通过MutableLiveData去实现的。
-
MutableLiveData既可以去改变值也可以监听值的变化但是为了数值的维护客观性(ps.不要直接通过MutableLiveData去setValue如果多个地方改变值不太容易看出来不方便后期),我们可以通过LiveData去写(MutableLiveData其实是继承自LiveData只不过就是暴露了下setValue跟postValue方法)
- 鉴于LiveData的setValue和postValue方法修饰关键词是protected
(ps.包名不同无法调用该方)
,我们可以这么去实现。
public class MyViewModel extends ViewModel {
private MutableLiveData<String> stringMediatorLiveData;
public LiveData<String> stringLiveData;
public MyViewModel() {
stringMediatorLiveData = new MutableLiveData<>();
stringLiveData = stringMediatorLiveData;
this.user = new User("张三",11);
this.count = 13;
}
public void update(String s){
stringMediatorLiveData.setValue(s);
}
}
- 使用
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.stringLiveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.e("MainActivity",s+"----");
}
});
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
viewModel.update("哈哈哈哈");
}
});
- 虽然多写了写代, 但是从代码维护来看更合理写(ps.官方也是建议这么做的)
-------------------------------分割线-------------------------------
如果就想去实时监听数据更新不管页面可见不可见那怎么办
- 这个时候observeForever方法就出马了
- 看了上面得源码部分就应该知道LiveData的observe接口是怎么根据可见不可见状态被调用的
-
observeForever方法直接把状态改成了全程可见,而不像observe方法由LifecycleBoundObserver根据shouldBeActive方法去设定了。
- 把observe改成observeForever方法就能实时去监听数值变化了,毕竟observeForever直接把状态设置成可见了,源码那快图8那个considerNotify方法就不会因为不可见状态return调了不去执行下面接口回调的方法了
Observer<Integer> integerObserver = new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Log.e("LiveDataActivity",string+"--->"+integer);
}
};
model.time.observeForever(integerObserver);
-
注意observeForever要自己去removeObserver掉监听,因为observe方法中LifecycleBoundObserver自己处理了
在扯下MediatorLiveData
MediatorLiveData是MutableLiveData的子类
-
MutableLiveData这个类是继承LiveData把postValue和setValue暴露出来
MediatorLiveData这个就有点意思了正如他的名字Mediator(中介的意思)
-
MediatorLiveData的核心是私有静态内部类Source
先记住红框的部分很重要,addSource就是把另一个LiveData由Source进行代理,监听当被代理的LiveData数值发生改变那么他就会去回调传入的观察者onChanged方法
原理很简单那有什么用呢
-
假设下面的场景实时知道文本框输入了多少个字
public MutableLiveData<String> message = new MutableLiveData<>();
public MediatorLiveData<Integer> messageNumber = new
MediatorLiveData<>();
messageNumber.setValue(0);
messageNumber.addSource(message, new Observer<String>() {
@Override
public void onChanged(String s) {
messageNumber.setValue(s.length());
}
});
<LinearLayout
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
<EditText
android:id="@+id/edit"
android:layout_width="200dp"
android:layout_marginLeft="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="@={mode.message}"
android:singleLine="true"
android:background="@drawable/b2"
android:layout_height="50dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:textColor="#cc00"
android:text='@{String.format("输入了%d个字",mode.messageNumber)}'
android:layout_height="wrap_content"/>
</LinearLayout>
- 这样实现就行了
- 当然绝对不能忘记注销LiveData的监听,原因嘛在observeForever也讲了的,我们去ViewModel的onCleared方法做就好了,毕竟onCleared是ViewModel执行的最后一个方法。
@Override
protected void onCleared() {
super.onCleared();
messageNumber.removeSource(message);
}
总结下
- 普通数据使用MutableLiveData和LiveData就行了
- 设计到数据联动的时候考虑下MediatorLiveData
最后在说1个东西转换
- 在项目中我们经常回运到数据转换的问题比如int转换成string或者int转换成另一个对象
map
public class LiveDataViewModel extends ViewModel {
private MutableLiveData<User> userMutableLiveData;
public LiveData<Integer> age;
private List<User> users;
public LiveDataViewModel() {
userMutableLiveData = new MutableLiveData<>();
age = Transformations.map(userMutableLiveData, new Function<User, Integer>() {
@Override
public Integer apply(User input) {
return input.getAge();
}
});
public void addUser(User user){
//这里值得注意的是setValue主线程用没问题,如果是子线程那就要用postValue
// userMutableLiveData.postValue(user);
if(Looper.myLooper() == Looper.getMainLooper()){
userMutableLiveData.setValue(user);
}else {
userMutableLiveData.postValue(user);
}
}
}
- 这时直接去监听age就行了,只要userMutableLiveData的值有改变我们就能立马监听到User类中的年龄了
model.age.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Log.e("MainActivity",integer+"--");
}
});
switchMap
- switchMap和map同样属于数据转换的一种,在实际项目中switchMap使用的频率回比map更高map是通过监听一个对象的变换然后进行转换的,假设有另外一个场景通过用户id进行网络请求去拿到这个用户的信息,按着map的写法岂不是要有好几个步骤先通过网络请求去拿到user对象、再去赋值给LiveData、在转换给LiveData、在对LiveData进行监听改变ui。
- 下面直接模拟下子线程查询进行用户查询
public class LiveDataViewModel extends ViewModel {
public LiveData<User> userLiveData;
private MutableLiveData<Integer> userIndex = new MutableLiveData<>();
private List<User> users;
public LiveDataViewModel() {
users = new ArrayList<>();
users.add(new User("李四",16));
users.add(new User("张三",18));
users.add(new User("王武",17));
users.add(new User("钱六",20));
userLiveData = Transformations.switchMap(userIndex, new Function<Integer, LiveData<User>>() {
@Override
public LiveData<User> apply(Integer input) {
return getUser(input);
}
});
}
public void queryUser(int userId){
userIndex.setValue(userId);
}
//模拟查询
public LiveData<User> getUser(int userId){
MutableLiveData<User> userMutableLiveData = new MutableLiveData<>();
if(userId>users.size()-1){
userMutableLiveData.setValue(new User("没有该用户",0));
return userMutableLiveData;
}
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
userMutableLiveData.postValue(users.get(userId));
}
}).start();
return userMutableLiveData;
}
}
- 监听
model.userLiveData.observe(this, new Observer<User>() {
@Override
public void onChanged(User user) {
dataBinding.name.setText("名字:"+user.getName());
dataBinding.tvAge.setText("年龄:"+user.getAge());
}
});
- 当调用了queryUser方法就会去触发switchMap方法中的Function接口apply方法去查询用户数据了
最后的最后说下onChanged回调是在主线程的,不然会抛异常这也解释了子线程得用postValue去更新数值
-
setValue
-
postValue