JetPack架構---LiveData的使用與示例

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

本文介紹:LiveData的單獨使用、LiveData與ViewModel結合使用、LiveData的複雜使用

一、LiveData的單獨使用

以一個整型數據爲例。將該整型數據用MutableLiveData包裝。

在Activity中,監聽count的變化:每次點擊按鈕,會生成隨機數,並Toast表現。

public class LiveDataActivity extends AppCompatActivity {
    //定義一個MutableLiveData包裝的Integer型數據count
    private MutableLiveData<Integer> count = new MutableLiveData<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live_data);
        //監聽count的變化
        count.observe(this, new Observer<Integer>() {
            @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<Integer> count = new MutableLiveData<>();

    public MutableLiveData<Integer> getCount() {
        return count;
    }
}

2-1:在頁面上使用該ViewModel

頁面上的ViewModel數據監聽

//獲取ViewModel
mainViewModel = new ViewModelProvider(this).get(MainViewModel.class);
//監聽ViewModel中的count數據變化
mainViewModel.getCount().observe(this, new Observer<Integer>() {
            @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<Person>用Map轉換後,只輸出LiveData<String>。

//類定義
public class Person {
    public String name;
    public int age;
}


//ViewModel定義
public class PersonViewModel extends ViewModel {
    private MutableLiveData<Person> person = new MutableLiveData<>();

    public MutableLiveData<Person> getPerson() {
        return person;
    }
}

------------以下爲頁面數據-----------
//ViewModel獲取
personViewModel =  new ViewModelProvider(this).get(PersonViewModel.class);
//數據轉換的規則定義:將Person類變爲只返回該類中的name
LiveData<String> map = Transformations.map(personViewModel.getPerson(), new Function<Person, String>() {
            @Override
            public String apply(Person input) {
                return input.name;
            }
});
//監聽數據變化
map.observe(this, new Observer<String>() {
            @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<String> stringLiveData = Transformations.switchMap(personViewModel.getPerson(), new Function<Person, LiveData<String>>() {
            @Override
            public LiveData<String> apply(Person input) {
                MutableLiveData test = new MutableLiveData<>();
                test.setValue(input.name);
                return test;
            }
});
//數據變化監聽
stringLiveData.observe(this, new Observer<String>() {
            @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<Integer> source1 = new MutableLiveData<>();
private MutableLiveData<Integer> source2 = new MutableLiveData<>();
//source整合兩個數據源
public MediatorLiveData<Integer> source = new MediatorLiveData<>();

//統一的監聽回調
private Observer<Integer> observer = new Observer<Integer>() {
        @Override
        public void onChanged(Integer s) {
            source.setValue(s);
        }
    };


-----------------頁面-----------------
//添加數據源
source.addSource(source1,observer);
source.addSource(source2,observer);
source.observe(this, new Observer<Integer>() {
            @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<String, MutableLiveData<Object>> 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 <T> MutableLiveData<T> with(String key,Class<T> type){
        if (!bus.containsKey(key)){
            bus.put(key,new BusMutableLiveData<>());
        }
        return (MutableLiveData<T>) bus.get(key);
    }

    //hook會反射方式複寫 observer中version的控制
    public static class BusMutableLiveData<T> extends MutableLiveData<T> {
        @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<LiveData> 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<String>() {
            @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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章