天天看點

Android LiveData 使用詳解

說在前面

本次推出 Android Architecture Components 系列文章,目前寫好了四篇,主要是關于 lifecycle,livedata 的使用和源碼分析,其餘的 Navigation, Paging library,Room,WorkMannager 等春節結束之後會更新,歡迎關注我的公衆号,有更新的話會第一時間會在公衆号上面通知。

目錄大概如下

1 LiveData 基本使用

2 自定義 Livedata

3 Livedata 共享資料

4 Livedata 小結

Android lifecycle 使用詳解

Android LiveData 使用詳解

Android lifecyle 源碼解剖

Android livedata 源碼解剖

github sample 位址: ArchiteComponentsSample

程式員徐公,四年中大廠經驗,一位不羁的碼農。

  1. 公衆号程式員徐公回複黑馬,擷取 Android 學習視訊
  2. 公衆号程式員徐公回複徐公666,擷取履歷模闆,教你如何優化履歷,走近大廠
  3. 公衆号程式員徐公回複面試,可以獲得面試常見算法,劍指 offer 題解
  4. 公衆号程式員徐公回複馬士兵,可以獲得馬士兵學習視訊一份
Android LiveData 使用詳解

前言

在上一篇部落格中,我們講解了 lifecycle 的使用及優點。這篇部落格讓我們一起來了解一下 LiveData 是怎樣使用的?

為什麼要引進 LiveData

LiveData 是一個可以被觀察的資料持有類,它可以感覺 Activity、Fragment或Service 等元件的生命周期。簡單來說,他主要有一下優點。

  1. 它可以做到在元件處于激活狀态的時候才會回調相應的方法,進而重新整理相應的 UI。
  2. 不用擔心發生記憶體洩漏
  3. 當 config 導緻 activity 重新建立的時候,不需要手動取處理資料的儲存和恢複。它已經幫我們封裝好了。
  4. 當 Actiivty 不是處于激活狀态的時候,如果你想 livedata setValue 之後立即回調 obsever 的 onChange 方法,而不是等到 Activity 處于激活狀态的時候才回調 obsever 的 onChange 方法,你可以使用 observeForever 方法,但是你必須在 onDestroy 的時候 removeObserver。

回想一下,在你的項目中,是不是經常會碰到這樣的問題,當網絡請求結果回來的時候,你經常需要判斷 Activity 或者 Fragment 是否已經 Destroy, 如果不是 destroy,才更新 UI。

而當你如果使用 Livedata 的話,因為它是在 Activity 處于 onStart 或者 onResume 的狀态時,他才會進行相應的回調,因而可以很好得處理這個問題,不必寫一大堆的 activity.isDestroyed()。接下來,讓我們一起來看一下 LiveData 的使用

LiveData 使用

基本使用

  1. 引入相關的依賴包
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.1.0"
// alternatively, just ViewModel
implementation "android.arch.lifecycle:viewmodel:1.1.0"
// alternatively, just LiveData
implementation "android.arch.lifecycle:livedata:1.1.0"
           
  1. 在代碼中使用

LiveData 是一個抽象類,它的實作子類有 MutableLiveData ,MediatorLiveData。在實際使用中,用得比較多的是 MutableLiveData。他常常結合 ViewModel 一起使用。下面,讓我們一起來看一下怎樣使用它?

首先,我們先寫一個類繼承我們的 ViewModel,裡面持有 mNameEvent。

public class TestViewModel extends ViewModel {

    private MutableLiveData<String> mNameEvent = new MutableLiveData<>();

    public MutableLiveData<String> getNameEvent() {
        return mNameEvent;
    }

}
           

接着,我們在 Activity 中建立 ViewModel,并監聽 ViewModel 裡面 mNameEvent 資料的變化,當資料改變的時候,我們列印相應的 log,并設定給 textView,顯示在界面上。這樣我們就完成了對 mNameEvent 資料源的觀察。

mTestViewModel = ViewModelProviders.of(this).get(TestViewModel.class);
MutableLiveData<String> nameEvent = mTestViewModel.getNameEvent();
nameEvent.observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
        Log.i(TAG, "onChanged: s = " + s);
        mTvName.setText(s);
    }
});
           

最後當我們資料源改變的時候,我們需要調用 livedata 的 setValue 或者 postvalue 方法。他們之間的差別是, 調用 setValue 方法,Observer 的 onChanged 方法會在調用 serValue 方法的線程回調。而

postvalue 方法,Observer 的 onChanged 方法将會在主線程回調。

可能部分同學有這樣的疑問了,我們的 ViewModel 是通過 ViewModelProviders.of(this).get(TestViewModel.class); 方法建立出來的,如果我們要攜帶參數,怎麼辦?

其實,官方也替我們考慮好了,同樣是調用 ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) 方法,隻不過,需要多傳遞一個 factory 參數。

Factory 是一個接口,它隻有一個 create 方法。

public interface Factory {
    /**
     * Creates a new instance of the given {@code Class}.
     * <p>
     *
     * @param modelClass a {@code Class} whose instance is requested
     * @param <T>        The type parameter for the ViewModel.
     * @return a newly created ViewModel
     */
    @NonNull
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
           

在實際當中,我們的做法是:實作 Factory 接口,重寫 create 方法,在create 方法裡面調用相應的構造函數,傳回相應的執行個體。

public class TestViewModel extends ViewModel {

    private final String mKey;
    private MutableLiveData<String> mNameEvent = new MutableLiveData<>();

    public MutableLiveData<String> getNameEvent() {
        return mNameEvent;
    }

    public TestViewModel(String key) {
        mKey = key;
    }

    public static class Factory implements ViewModelProvider.Factory {
        private String mKey;

        public Factory(String key) {
            mKey = key;
        }

        @Override
        public <T extends ViewModel> T create(Class<T> modelClass) {
            return (T) new TestViewModel(mKey);
        }
    }

    public String getKey() {
        return mKey;
    }
}
           

ViewModelProviders.of(this, new TestViewModel.Factory(mkey)).get(TestViewModel.class)

自定義 Livedata

Livedata 主要有幾個方法

  1. observe
  2. onActive
  3. onInactive
  4. observeForever

void observe (LifecycleOwner owner, Observer observer)

Adds the given observer to the observers list within the lifespan of the given owner. The events are dispatched on the main thread. If LiveData already has data set, it will be delivered to the observer.

void onActive ()

Called when the number of active observers change to 1 from 0.

This callback can be used to know that this LiveData is being used thus should be kept up to date.

當回調該方法的時候,表示該 liveData 正在背使用,是以應該保持最新

void onInactive ()

Called when the number of active observers change from 1 to 0.

This does not mean that there are no observers left, there may still be observers but their lifecycle states aren’t STARTED or RESUMED (like an Activity in the back stack).

You can check if there are observers via hasObservers().

當該方法回調時,表示他所有的 obervers 沒有一個狀态處理 STARTED 或者 RESUMED,注意,這不代表沒有 observers。

Void observeForever

跟 observe 方法不太一樣的是,它在 Activity 處于 onPause ,onStop, onDestroy 的時候,都可以回調 obsever 的 onChange 方法,但是有一點需要注意的是,我們必須手動 remove obsever,否則會發生記憶體洩漏。

這裡我們以觀察網絡狀态變化為例子講解

  1. 首先我們自定義一個 Class NetworkLiveData,繼承 LiveData,重寫它的 onActive 方法和 onInactive 方法
  2. 在 onActive 方法中,我們注冊監聽網絡變化的廣播,即ConnectivityManager.CONNECTIVITY_ACTION。在 onInactive 方法的時候,我們登出廣播。
public class NetworkLiveData extends LiveData<NetworkInfo> {

    private final Context mContext;
    static NetworkLiveData mNetworkLiveData;
    private NetworkReceiver mNetworkReceiver;
    private final IntentFilter mIntentFilter;

    private static final String TAG = "NetworkLiveData";

    public NetworkLiveData(Context context) {
        mContext = context.getApplicationContext();
        mNetworkReceiver = new NetworkReceiver();
        mIntentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
    }

    public static NetworkLiveData getInstance(Context context) {
        if (mNetworkLiveData == null) {
            mNetworkLiveData = new NetworkLiveData(context);
        }
        return mNetworkLiveData;
    }

    @Override
    protected void onActive() {
        super.onActive();
        Log.d(TAG, "onActive:");
        mContext.registerReceiver(mNetworkReceiver, mIntentFilter);
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        Log.d(TAG, "onInactive: ");
        mContext.unregisterReceiver(mNetworkReceiver);
    }

    private static class NetworkReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            ConnectivityManager manager = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
            getInstance(context).setValue(activeNetwork);

        }
    }
}
           

這樣,當我們想監聽網絡變化的時候,我們隻需要調用相應的 observe 方法即可,友善又快捷。

NetworkLiveData.getInstance(this).observe(this, new Observer<NetworkInfo>() {
    @Override
    public void onChanged(@Nullable NetworkInfo networkInfo) {
        Log.d(TAG, "onChanged: networkInfo=" +networkInfo);
    }
});

           

https://www.jianshu.com/p/4b7945475a6f

共享資料

Fragment Activity 之間共享資料

我們回過頭來再來看一下 ViewModelProvider 的 of 方法,他主要有四個方法,分别是

  1. ViewModelProvider of(@NonNull Fragment fragment)
  2. ViewModelProvider of(@NonNull FragmentActivity activity)
  3. ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory)
  4. ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory)

1,2 方法之間的主要差別是傳入 Fragment 或者 FragmentActivity。而我們知道,通過 ViewModel of 方法建立的 ViewModel 執行個體, 對于同一個 fragment 或者 fragmentActivity 執行個體,ViewModel 執行個體是相同的,因而我們可以利用該特點,在 Fragment 中建立 ViewModel 的時候,傳入的是 Fragment 所依附的 Activity。因而他們的 ViewModel 執行個體是相同的,進而可以做到共享資料。

// LiveDataSampleActivity(TestFragment 依賴的 Activity)
mTestViewModel = ViewModelProviders.of(this, new TestViewModel.Factory(mkey)).get(TestViewModel.class);
MutableLiveData<String> nameEvent = mTestViewModel.getNameEvent();
nameEvent.observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
        Log.i(TAG, "onChanged: s = " + s);
        mTvName.setText(s);
    }
});


// TestFragment 中
mViewModel = ViewModelProviders.of(mActivity).get(TestViewModel.class);
mViewModel.getNameEvent().observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
        Log.d(TAG, "onChanged: s =" + s + " mViewModel.getKey() =" + mViewModel.getKey());
        mTvName.setText(s);
        boolean result = mViewModel == ((LiveDataSampleActivity) mListener).mTestViewModel;
        Log.d(TAG, "onChanged: s result =" + result);
    }
});
           

這樣,LiveDataSampleActivity 和 TestFragment 中的 ViewModel 是同一個執行個體。即 Activity 和 Fragment 共享資料。

全局共享資料

說到全局共享資料,我們想一下我們的應用全景,比如說我的賬戶資料,這個對于整個 App 來說,肯定是全局共享的。有時候,當我們的資料變化的時候,我們需要通知我們相應的界面,重新整理 UI。如果用傳統的方式來實作,那麼我們一般才采取觀察者的方式來實作,這樣,當我們需要觀察資料的時候,我們需要添加 observer,在界面銷毀的時候,我們需要移除 observer。

但是,如果我們用 LiveData 來實作的話,它内部邏輯都幫我們封裝好了,我們隻需要保證 AccountLiveData 是單例的就ok,在需要觀察的地方調用 observer 方法即可。也不需要手動移除 observer,不會發生記憶體洩漏,友善快捷。

這裡 AccountLiveData 的實作就不貼出來了,可以參考上面的 NetworkLiveData 實作

小結

這裡說一點關于 LiveData 與 ViewModel 的應用場景,我盡量說得通俗一點,不要說得那麼官方,這樣對新手很難了解。

覺得不錯的,請點個贊,讓我們看到你們的歡呼聲。你們的支援就是我寫作的最大動力。

  1. LiveData 内部已經實作了觀察者模式,如果你的資料要同時通知幾個界面,可以采取這種方式
  2. 我們知道 LiveData 資料變化的時候,會回調 Observer 的 onChange 方法,但是回調的前提是 lifecycleOwner(即所依附的 Activity 或者 Fragment) 處于 started 或者 resumed 狀态,它才會回調,否則,必須等到 lifecycleOwner 切換到前台的時候,才回調。
  3. 是以,這對性能方面确實是一個不小的提升。但是,對于你想做一些類似與在背景工作的(黑科技), liveData 就不太适合了,你可以使用 observeForever 方法,或者自己實作觀察者模式去吧。

Lifecycle,LiveData, ViewModel 的基本使用到此已經講解完畢,想了解他們的實作原理的話可以閱讀這兩篇文章。

Android lifecyle 源碼解剖

Android livedata 源碼解剖

github sample 位址: ArchiteComponentsSample

推薦閱讀

Android 啟動優化(一) - 有向無環圖

Android 啟動優化(二) - 拓撲排序的原理以及解題思路

Android 啟動優化(三)- AnchorTask 開源了

Android 啟動優化(四)- AnchorTask 是怎麼實作的

Android 啟動優化(五)- AnchorTask 1.0.0 版本正式釋出了

Android 啟動優化(六)- 深入了解布局優化

這幾篇文章從 0 到 1,講解 DAG 有向無環圖是怎麼實作的,以及在 Android 啟動優化的應用。

推薦理由:現在挺多文章一談到啟動優化,動不動就聊拓撲結構,這篇文章從資料結構到算法、到設計都給大家說清楚了,開源項目也有非常強的借鑒意義。

Android LiveData 使用詳解

繼續閱讀