Android架構元件(二)——LiveData
上一篇文章講到了Android架構元件之一Lifecycle元件(Android 架構元件(一)——Lifecycle-Aware Components,現在我們再來看看另一個成員LiveData。
定義
簡單地說,LiveData是一個資料持有類。它具有以下特點:
- 資料可以被觀察者訂閱;
- 能夠感覺元件(Fragment、Activity、Service)的生命周期;
-
隻有在元件出于激活狀态(STARTED、RESUMED)才會通知觀察者有資料更新;
PS: 文中提到的“元件”皆指實作了LifecycleOwner接口Fragment、Activity。
為什麼需要LiveData
從LiveData具有的特點,我們就能聯想到它能夠解決我們遇到的什麼問題。LiveData具有以下優點:
-
能夠保證資料和UI統一
這個和LiveData采用了觀察者模式有關,LiveData是被觀察者,當資料有變化時會通知觀察者(UI)。
-
減少記憶體洩漏
這是因為LiveData能夠感覺到元件的生命周期,當元件處于DESTROYED狀态時,觀察者對象會被清除掉。
-
當Activity停止時不會引起崩潰
這是因為元件處于非激活狀态時,不會收到LiveData中資料變化的通知。
-
不需要額外的手動處理來響應生命周期的變化
這一點同樣是因為LiveData能夠感覺元件的生命周期,是以就完全不需要在代碼中告訴LiveData元件的生命周期狀态。
-
元件和資料相關的内容能實時更新
元件在前台的時候能夠實時收到資料改變的通知,這是可以了解的。當元件從背景到前台來時,LiveData能夠将最新的資料通知元件,這兩點就保證了元件中和資料相關的内容能夠實時更新。
-
針對configuration change時,不需要額外的處理來儲存資料
我們知道,當你把資料存儲在元件中時,當configuration change(比如語言、螢幕方向變化)時,元件會被recreate,然而系統并不能保證你的資料能夠被恢複的。當我們采用LiveData儲存資料時,因為資料群組件分離了。當元件被recreate,資料還是存在LiveData中,并不會被銷毀。
-
資源共享
通過繼承LiveData類,然後将該類定義成單例模式,在該類封裝監聽一些系統屬性變化,然後通知LiveData的觀察者,這個在繼承LiveData中會看到具體的例子。
LiveData使用
在了解LiveData定義和優點後,那它到底怎麼應用呢?LiveData有幾種使用方式:
- 使用LiveData對象
- 繼承LiveData類
使用LiveData對象
使用LiveData對象主要有以下幾個步驟:
- 建立儲存特定資料類型的LiveData執行個體;
- 建立Observer對象,作為參數傳入LiveData.observe()方法添加觀察者;
- 更新Livedata對象存儲的資料;
建立LiveData執行個體
Android文檔中建議LiveData配合ViewModel使用更加哦,其實呢,你也可以不使用ViewModel,但是一定要做到LiveData中儲存的資料群組件分離,至于原因,前面我們已經提到過了。下面是在ViewModel中建立LiveData執行個體的例子:
/**
* Created by shymanzhu on 2017/12/20.
*/
public class NameViewModel extends ViewModel{
// Create a LiveData with a String
private MutableLiveData<String> mCurrentName;
// Create a LiveData with a String list
private MutableLiveData<List<String>> mNameListData;
public MutableLiveData<String> getCurrentName() {
if (mCurrentName == null) {
mCurrentName = new MutableLiveData<>();
}
return mCurrentName;
}
public MutableLiveData<List<String>> getNameList(){
if (mNameListData == null) {
mNameListData = new MutableLiveData<>();
}
return mNameListData;
}
}
在NameViewModel中建立了兩個MutableLiveData(MutableLiveData是LiveData的子類)執行個體,分别存儲目前姓名、姓名清單;兩個執行個體通過NameViewModel中的getter方法得到。
建立Observer對象,添加觀察者
/**
* Created by shymanzhu on 2017/12/19.
*/
public class LiveDataFragment extends Fragment {
private static final String TAG = "LiveDataFragment";
private NameViewModel mNameViewModel;
@BindView(R.id.tv_name)
TextView mTvName;
public static LiveDataFragment getInstance(){
return new LiveDataFragment();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNameViewModel = ViewModelProviders.of(this).get(NameViewModel.class);
mNameViewModel.getCurrentName().observe(this,(String name) -> {
mTvName.setText(name);
Log.d(TAG, "currentName: " + name);
}); // 訂閱LiveData中目前Name資料變化,以lambda形式定義Observer
mNameViewModel.getNameList().observe(this, (List<String> nameList) -> {
for (String item : nameList) {
Log.d(TAG, "name: " + item);
}
}); // 訂閱LiveData中Name清單資料變化,以lambda形式定義Observer
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_livedata, container, false);
ButterKnife.bind(this, view);
return view;
}
}
在onCreate()方法中通過LiveData.observe()方法添加觀察者,當資料變化時會通過回調方法通知觀察者,在lambda表達式中更新目前姓名和列印姓名清單。
更新LiveData中的資料
在上面我們已經訂閱了LiveData資料變化,現在我們看下如果LiveData資料變化時,上面的lambda表達式中是否會受到更新的通知。我們在LiveDataFragment中增加兩個按鈕來改變LiveData中的資料。
@OnClick({R.id.btn_change_name, R.id.btn_update_list})
void onClicked(View view){
switch (view.getId()){
case R.id.btn_change_name:
mNameViewModel.getCurrentName().setValue("Jane");
break;
case R.id.btn_update_list:
List<String> nameList = new ArrayList<>();
for (int i = 0; i < 10; i++){
nameList.add("Jane<" + i + ">");
}
mNameViewModel.getNameList().setValue(nameList);
break;
}
}
代碼很簡單,在兩個按鈕的點選事件中通過LiveData.setValue()方法來改變LiveData中儲存的資料。當點選這兩個按鈕的時候,我們會發現在onCreate()方法中會收相應到資料改變的回調。
繼承LiveData類
除了直接使用LiveDatad對象外,我們還可以通過內建LiveData類來定義适合特定需求的LiveData。下面繼承LiveData類的例子,驗證下LiveData的其中一個優點——資源共享。
/**
* Created by shymanzhu on 2017/12/19.
*/
public class MyLiveData extends LiveData<Integer> {
private static final String TAG = "MyLiveData";
private static MyLiveData sData;
private WeakReference<Context> mContextWeakReference;
public static MyLiveData getInstance(Context context){
if (sData == null){
sData = new MyLiveData(context);
}
return sData;
}
private MyLiveData(Context context){
mContextWeakReference = new WeakReference<>(context);
}
@Override
protected void onActive() {
super.onActive();
registerReceiver();
}
@Override
protected void onInactive() {
super.onInactive();
unregisterReceiver();
}
private void registerReceiver() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
mContextWeakReference.get().registerReceiver(mReceiver, intentFilter);
}
private void unregisterReceiver() {
mContextWeakReference.get().unregisterReceiver(mReceiver);
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "action = " + action);
if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
int wifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
int wifiLevel = WifiManager.calculateSignalLevel(
wifiRssi, 4);
sData.setValue(wifiLevel);
}
}
};
}
MyLiveData是個繼承了LiveData的單例類,在onActive()和onInactive()方法中分别注冊和反注冊Wifi信号強度的廣播。然後在廣播接收器中更新MyLiveData對象。在使用的時候就可以通過MyLiveData.getInstance()方法,然後通過調用observe()方法來添加觀察者對象,訂閱Wifi資訊強度變化。
- onActive(),此方法是當處于激活狀态的observer個數從0到1時,該方法會被調用。
- onInactive() ,此方法是當處于激活狀态的observer個數從1變為0時,該方法會被調用。
LiveDta原理
對于某個元件的原了解析,個人現在比較習慣于從類圖、時序、源碼幾個方面着手分析。下面的内容也是從這幾點依次展開的
類關系圖
LiveData的類關系圖相對比較簡單,從上面的類圖我們就能看到。和LiveData元件相關的類和接口有:LiveData類、Observer接口、GenericLifecycleObserver接口。LiveData類是個抽象類,但是它沒有抽象方法,抽象類有個特點是:不能在抽象類中執行個體化自己。為什麼LiveData會被定義成abstract而又沒有抽象方法呢,這個…我也不知道,看了下LiveData的送出記錄,是在将hasObservers()替換getObserverCount()方法時将LiveData改成了abstract,在此之前它是被定義為public,可以翻牆的可以看下這裡的修改記錄
- MediatorLiveData繼承自MutableLiveData,MutableLiveData繼承自LiveData。MediatorLiveData可以看成是多個LiveData的代理,當将多個LiveData添加到MediatorLiveData,任何一個LiveData資料發生變化時,MediatorLiveData都會收到通知。
- LiveData有個内部類LifecycleBoundObserver,它實作了GenericLifecycleObserver,而GenericLifecycleObserver繼承了LifecycleObserver接口。在這裡可以回顧下Lifecycle元件相關的内容。當元件(Fragment、Activity)生命周期變化時會通過onStateChanged()方法回調過來。
-
Observer接口就是觀察者,其中定義了LiveData資料變化的回調方法onChanged()。
時序圖
LiveData主要涉及到的時序有三個:
- 在Fragment/Activity中通過LiveData.observer()添加觀察者(observer()方法中的第二個參數)。
- 根據Fragment/Activity生命周期發生變化時,移除觀察者或者通知觀察者更新資料。
- 當調用LiveData的setValue()、postValue()方法後,通知觀察者更新資料。
源碼解析
根據LiveData主要涉及到的三個時序的内容,我們通過源碼看下它具體的實作。
添加觀察者
LiveData提供了兩種添加觀察者的方法:observeForever()、observe()。
- observeForever()
@MainThread
public void observeForever(@NonNull Observer<T> observer) {
observe(ALWAYS_ON, observer);
}
從方法的命名,我們也能對它的功能略知一二,通過observeForever()添加觀察者,觀察者會一直受到資料的變化回到,而不是在元件處于STARTED和RESUMED狀态下才會收到,因為這是LifecycleOwner對象就不再是元件了,而是ALWAYS_ON;另外通過該方法添加觀察者後,要手動調用removeObserver()方法來停止觀察者接收回調通知。observeForever()方法體很簡單,調用了observe()方法,傳入的一個參數是ALWAYS_ON常量,重點看下ALWAYS_ON常量是個啥東東。
private static final LifecycleOwner ALWAYS_ON = new LifecycleOwner() {
private LifecycleRegistry mRegistry = init();
private LifecycleRegistry init() {
LifecycleRegistry registry = new LifecycleRegistry(this);
registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
registry.handleLifecycleEvent(Lifecycle.Event.ON_START);
registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
return registry;
}
@Override
public Lifecycle getLifecycle() {
return mRegistry;
}
};
ALWAYS_ON是LifecycleOwner常量,在init方法中會初始化Lifecycle的生命周期狀态,完了之後,就沒有改變過Lifecycle的生命周期狀态了,這也就是為什麼通過observeForever()添加觀察者是,當資料改變時不管元件處于什麼狀态都會收到回調的原因,除非手動将觀察者移除。
- observe()
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//将LifecycleOwner對象和Observer對象封裝成LifecycleBoundObserver對象。
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// mObservers可以了解成一個類似Map的容器,putIfAbsent()方法是判斷容器中的observer(key)
// 是否有已經和wrapper(value)關聯,如果已經關聯則傳回關聯值,否則關聯并傳回wrapper。
LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && existing.owner != wrapper.owner) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper); //條件LifecycleOwner的生命周期觀察者
}
該方法也比較簡單,主要邏輯都在注釋中說明了,就不再贅述了。
元件(Fragment/Activity)生命周期發生變化
在LiveData.observe()方法中添加了元件(實作了LifecycleOwner接口的Fragment和Activity)生命周期觀察者。而這個觀察者就是LifecycleBoundObserver對象,下面我們看下LifecycleBoundObserver具體實作。
class LifecycleBoundObserver implements GenericLifecycleObserver {
public final LifecycleOwner owner;
public final Observer<T> observer;
public boolean active;
public int lastVersion = START_VERSION;
LifecycleBoundObserver(LifecycleOwner owner, Observer<T> observer) {
this.owner = owner;
this.observer = observer;
}
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
// LifecycleOwner對象生命周期發生變化時,會通過該回調方法通知過來。
// 如果目前處于Lifecycle.State.DESTROYED時,會自動将觀察者移除。
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(observer);
return;
}
// 判斷是否處于actived狀态,并将結果作為參數傳遞給activeStateChanged()
activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
}
void activeStateChanged(boolean newActive) {
if (newActive == active) { //新狀态和之前狀态相同,不處理
return;
}
active = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += active ? 1 : -1;
if (wasInactive && active) { //處于激活狀态的observer個數從0到1
onActive();
}
if (LiveData.this.mActiveCount == 0 && !active) { //處于激活狀态的observer個數從1變為0
onInactive();
}
if (active) {
dispatchingValue(this);
}
}
}
通過observe()方法添加觀察者時,當元件(Fragment/Activity)生命周期發生變化時,onStateChanged()方法會被調用。如果通過observeForever()方法添加觀察者時,隻有在常量ALWAYS_ON初始化的時候,onStateChanged()方法會被調用三次(CREATED、STARTED、RESUMED),後面就不會收到DESTROYED的狀态,這也就是為什麼要通過removeObserver()方法手動移除觀察者的原因。
onActive()和onInactive()都是空實作的方法,繼承類可以選擇去實作。我們看下dispatchingValue()方法的具體實作。
private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) { // initiator不為空,考慮通知回調
considerNotify(initiator);
initiator = null;
} else { // initiator為空,考慮通知mObservers容器中對象回調
for (Iterator<Map.Entry<Observer<T>, LifecycleBoundObserver>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
private void considerNotify(LifecycleBoundObserver observer) {
if (!observer.active) {
return;
}
if (!isActiveState(observer.owner.getLifecycle().getCurrentState())) {
observer.activeStateChanged(false);
return;
}
if (observer.lastVersion >= mVersion) {
return;
}
observer.lastVersion = mVersion;
// 最終回調的地方,也就是調用observe()或者observeForever()是傳入的Observer對象。
observer.observer.onChanged((T) mData);
}
改變LiveData資料
LiveData提供了兩種改變資料的方法:setValue()和postValue()。差別是setValue()要在主線程中調用,而postValue()既可在主線程也可在子線程中調用。我們先看setValue()方法的具體實作:
@MainThread
protected void setValue(T value) {
assertMainThread("setValue"); //判斷目前線程是否是主線程,不是主線程就抛出異常
mVersion++;
mData = value;
dispatchingValue(null);
}
再看下postValue()方法的具體實作:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
// 會在主線程中執行 mPostValueRunnable中的内容。
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
// 在主線程中調用setValue()方法
setValue((T) newValue);
}
};
postValue()方法通過ArchTaskExecutor實作在主線程中執行mPostValueRunnable對象中的内容,而在mPostValueRunnable中最終會調用setValue()方法來實作改變LiveData存儲的資料。
總結
整個流程下來,你會發現其實LiveData的原理還是蠻簡單的。當然LiveData還有更多的使用方法,具體的内容隻有在你使用到的時候再去研究吧,我先抛磚引玉,剩下的就看你們的啦…