JetPack架构---LiveData的使用与示例

点此,查看 :LiveData的原理解析

本文介绍:LiveData的单独使用、LiveData与ViewModel结合使用、LiveData的复杂使用

一、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中定义的变量等

二、LiveData结合ViewModel使用

ViewModel作为页面数据的存储空间。给相应的页面定义相应的ViewModel,管理该页面的所有数据。

2-0:定义一个ViewModel

public class MainViewModel extends ViewModel {
    private MutableLiveData count = new MutableLiveData<>();

    public MutableLiveData getCount() {
        return count;
    }
}

2-1:在页面上使用该ViewModel

页面上的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的使用进阶

3-0:LiveData数据转型---map

用于将该LiveData中的数据做一次过滤或者转变,生成一个新的LiveData。以一个Person类作为此次例子,该类含有name与age两个属性。将LiveData用Map转换后,只输出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);
            }
});

3-1:LiveData的数据转型---switchMap

//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中调用方法从网络或者数据库获取数据。

3-2:MediatorLiveData的使用

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可用于网络与本地数据库同时存在,有两个数据源时,无论二者中,哪部分数据发生变化,都能起效。

3-3:LiveData的封装---LiveDataBus

应用中,存在多个页面之间,都会共用一个或者一组数据的情况。

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 observer) {
            super.observe(owner, observer);
            try {
                hook(observer);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 反射技术  使observer.mLastVersion = mVersion
         *
         * @param observer ob
         */
        private void hook(Observer 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。

你可能感兴趣的:(Android技巧)