天天看點

Android架構元件(二)——LiveDataAndroid架構元件(二)——LiveData

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對象主要有以下幾個步驟:

  1. 建立儲存特定資料類型的LiveData執行個體;
  2. 建立Observer對象,作為參數傳入LiveData.observe()方法添加觀察者;
  3. 更新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原理

 對于某個元件的原了解析,個人現在比較習慣于從類圖、時序、源碼幾個方面着手分析。下面的内容也是從這幾點依次展開的

類關系圖

Android架構元件(二)——LiveDataAndroid架構元件(二)——LiveData

 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()。

時序圖

Android架構元件(二)——LiveDataAndroid架構元件(二)——LiveData

 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還有更多的使用方法,具體的内容隻有在你使用到的時候再去研究吧,我先抛磚引玉,剩下的就看你們的啦…

繼續閱讀