点此,查看 :LiveData的原理解析
本文介绍:LiveData的单独使用、LiveData与ViewModel结合使用、LiveData的复杂使用
以一个整型数据为例。将该整型数据用MutableLiveData包装。
在Activity中,监听count的变化:每次点击按钮,会生成随机数,并Toast表现。
public class LiveDataActivity extends AppCompatActivity {
//定义一个MutableLiveData包装的Integer型数据count
private MutableLiveData count = new MutableLiveData<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_data);
//监听count的变化
count.observe(this, new Observer() {
@Override
public void onChanged(Integer integer) {
//展示数据的变化
Toast.makeText(LiveDataActivity.this, String.valueOf(integer), Toast.LENGTH_SHORT).show();
}
});
findViewById(R.id.liveDataOnlyTest).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//驱动数据变化
count.setValue(new Random().nextInt());
}
});
}
}
该场景也可以扩展为:监听全局变量的数据变化,如Application中定义的变量等
ViewModel作为页面数据的存储空间。给相应的页面定义相应的ViewModel,管理该页面的所有数据。
public class MainViewModel extends ViewModel {
private MutableLiveData count = new MutableLiveData<>();
public MutableLiveData getCount() {
return count;
}
}
页面上的ViewModel数据监听
//获取ViewModel
mainViewModel = new ViewModelProvider(this).get(MainViewModel.class);
//监听ViewModel中的count数据变化
mainViewModel.getCount().observe(this, new Observer() {
@Override
public void onChanged(Integer integer) {
Toast.makeText(LiveDataActivity.this, "ViewModel+LiveData示例数据变化:" + integer, Toast.LENGTH_SHORT).show();
}
});
......
//驱动数据变化
findViewById(R.id.liveDataWithViewModel).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mainViewModel.getCount().setValue(new Random().nextInt());
}
});
用于将该LiveData中的数据做一次过滤或者转变,生成一个新的LiveData。以一个Person类作为此次例子,该类含有name与age两个属性。将LiveData
//类定义
public class Person {
public String name;
public int age;
}
//ViewModel定义
public class PersonViewModel extends ViewModel {
private MutableLiveData person = new MutableLiveData<>();
public MutableLiveData getPerson() {
return person;
}
}
------------以下为页面数据-----------
//ViewModel获取
personViewModel = new ViewModelProvider(this).get(PersonViewModel.class);
//数据转换的规则定义:将Person类变为只返回该类中的name
LiveData map = Transformations.map(personViewModel.getPerson(), new Function() {
@Override
public String apply(Person input) {
return input.name;
}
});
//监听数据变化
map.observe(this, new Observer() {
@Override
public void onChanged(String s) {
Toast.makeText(LiveDataActivity.this, "Person过滤转型后得到的名字:" + s, Toast.LENGTH_SHORT).show();
}
});
//驱动数据变化
findViewById(R.id.liveDataWithViewModelWhemMap).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Person person = new Person();
person.name = "晓明";
person.age = 18;
personViewModel.getPerson().setValue(person);
}
});
//ViewModel定义
personViewModel = new ViewModelProvider(this).get(PersonViewModel.class);
//switchMap转换,与map不同的是,该方法的apply需要返回一个新的LiveData格式的数据。
LiveData stringLiveData = Transformations.switchMap(personViewModel.getPerson(), new Function>() {
@Override
public LiveData apply(Person input) {
MutableLiveData test = new MutableLiveData<>();
test.setValue(input.name);
return test;
}
});
//数据变化监听
stringLiveData.observe(this, new Observer() {
@Override
public void onChanged(String s) {
Toast.makeText(LiveDataActivity.this, "Person经SwitchMap转型后得到的名字:" + s, Toast.LENGTH_SHORT).show();
}
});
//驱动数据变化
findViewById(R.id.liveDataWithViewModelWhemSwitchMap).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Person person = new Person();
person.name = "晓红";
person.age = 18;
personViewModel.getPerson().setValue(person);
}
});
switchMap方法中,apply返回的是一个LiveData类型的数据。这主要是在这样的场景中使用的:ViewModel中定义的LiveData,命名为A。A的数据不是在ViewModel中直接组装的,而是要通过调用其他方法获取的。此时,用switchMap将获取到的LiveData,赋值给 A。
常见的场景有:ViewModel中调用方法从网络或者数据库获取数据。
MediatorLiveData是LiveData的子类,可以合并多个LiveData数据源,当任意一个数据源数据发生变化,MediatorLiveData的监听都将起效。
//source1与source2作为两个数据源
private MutableLiveData source1 = new MutableLiveData<>();
private MutableLiveData source2 = new MutableLiveData<>();
//source整合两个数据源
public MediatorLiveData source = new MediatorLiveData<>();
//统一的监听回调
private Observer observer = new Observer() {
@Override
public void onChanged(Integer s) {
source.setValue(s);
}
};
-----------------页面-----------------
//添加数据源
source.addSource(source1,observer);
source.addSource(source2,observer);
source.observe(this, new Observer() {
@Override
public void onChanged(Integer integer) {
Toast.makeText(LiveDataActivity.this, "--->"+integer, Toast.LENGTH_SHORT).show();
}
});
----------------驱动数据--------------
//无论是source1还是source2的数据变化,都将触发回调
source1.postValue(1);
source2.setValue(2);
MediatorLiveData可用于网络与本地数据库同时存在,有两个数据源时,无论二者中,哪部分数据发生变化,都能起效。
应用中,存在多个页面之间,都会共用一个或者一组数据的情况。
1、如ActivityA,ActivityB都会同时监听某个数据的变化,需要根据这个数据的变化,做某些操作。
2、组件之间同时监听同一数据的变化。
3、组件间的通讯。
以上几种情况都可以通过封装LiveData,实现一个LiveData的通讯框架来实现。命名这个框架为LiveDataBus。
这个框架同时也会解决前文:JetPack架构---LiveData特性、原理 中提到的“为什么LiveData会带有粘性特制”,把该特性屏蔽,避免额外问题。
LiveDataBus的实现只需一个类,如下(部分引用自网络),该类简单的说,就是一个Map,用于存储多个LiveData。
public class LiveDataBus {
private static volatile LiveDataBus INSTANCE;
private final Map> bus;
private LiveDataBus() {
bus = new HashMap<>();
}
public static LiveDataBus get(){
if(INSTANCE == null){
synchronized (LiveDataBus.class){
INSTANCE = new LiveDataBus();
}
}
return INSTANCE;
}
//此处type用于后续可能的observe或者post/set value
//此处key用于生成或者获取,该key对应的LiveData
public synchronized MutableLiveData with(String key,Class type){
if (!bus.containsKey(key)){
bus.put(key,new BusMutableLiveData<>());
}
return (MutableLiveData) bus.get(key);
}
//hook会反射方式复写 observer中version的控制
public static class BusMutableLiveData extends MutableLiveData {
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer super T> observer) {
super.observe(owner, observer);
try {
hook(observer);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 反射技术 使observer.mLastVersion = mVersion
*
* @param observer ob
*/
private void hook(Observer super T> observer) throws Exception {
//根据源码 如果使observer.mLastVersion = mVersion; 就不会走 回调OnChange方法了,所以就算注册
//也不会收到消息
//首先获取liveData的class
Class classLiveData = LiveData.class;
//通过反射获取该类里mObserver属性对象
Field fieldObservers = classLiveData.getDeclaredField("mObservers");
//设置属性可以被访问
fieldObservers.setAccessible(true);
//获取的对象是this里这个对象值,他的值是一个map集合
Object objectObservers = fieldObservers.get(this);
//获取map对象的类型
Class> classObservers = objectObservers.getClass();
//获取map对象中所有的get方法
Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
//设置get方法可以被访问
methodGet.setAccessible(true);
//执行该get方法,传入objectObservers对象,然后传入observer作为key的值
Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
//定义一个空的object对象
Object objectWrapper = null;
//判断objectWrapperEntry是否为Map.Entry类型
if (objectWrapperEntry instanceof Map.Entry) {
objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
}
if (objectWrapper == null) {
throw new NullPointerException("Wrapper can not be null!");
}
//如果不是空 就得到该object的父类
Class> classObserverWrapper = objectWrapper.getClass().getSuperclass();
//通过他的父类的class对象,获取mLastVersion字段
Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
fieldLastVersion.setAccessible(true);
Field fieldVersion = classLiveData.getDeclaredField("mVersion");
fieldVersion.setAccessible(true);
Object objectVersion = fieldVersion.get(this);
//把mVersion 字段的属性值设置给mLastVersion
fieldLastVersion.set(objectWrapper, objectVersion);
}
}
}
LiveDataBus的使用方式很简便:
//LiveData监听,该LiveData的识别码是Define.STR_KEY,该值随意定义
LiveDataBus.get().with(Define.STR_KEY, String.class).observe(Main2Activity.this, new Observer() {
@Override
public void onChanged(String info) {
Log.e(TAG, "onChanged: " + info);
}
});
//LiveData,驱动数据变化
LiveDataBus.get().with(Define.INFO_KEY, String.class).postValue(info);
LiveData从入门用法、与ViewModel结合,到后续的转换、封装。基本囊括了目前LiveData的使用方式。如需扩展等,仍然可以根据LiveData的特性,自定义相应的LiveData。