Android框架組件--LiveData的使用

1.前言

LiveData是Google推出的一系列的框架組件的其中一個,它是一個可以被觀察的數(shù)據(jù)持有類滥沫,能夠感知ActivityFragment等組件的生命周期。

一個觀察者去觀察LiveData后,當(dāng)觀察者的生命周期處于STARTEDRESUMED狀態(tài)時(即onStart()onResume()onPause()),LiveData的數(shù)據(jù)發(fā)生變化屎媳,則會通知觀察者嘉汰;若觀察者處于其他狀態(tài),即使LiveData的數(shù)據(jù)發(fā)生變化荧降,也不會發(fā)出通知接箫。

正是由于這一特性,因此LiveData可以做到僅在組件處于活躍狀態(tài)時才進(jìn)行更新UI的操作朵诫。

使用LiveData前需要先了解Lifecycle辛友,如果還不知道Lifecycle,可以看下這篇文章:Android框架組件--Lifecycle的使用

本文主要介紹如何使用LiveData剪返。

2.LiveData使用例子

下面來看下如何使用LiveData废累。

3.1 添加依賴

在相應(yīng)的moudle目錄下的build.gradle中添加以下依賴:

dependencies {
    //...
    def lifecycle_version = "1.1.1"
    //僅僅依賴LiveData
    implementation "android.arch.lifecycle:livedata:$lifecycle_version"
}

3.2 創(chuàng)建LiveData對象

google官網(wǎng)提倡LiveData配合ViewModel一起使用。為了專注LiveData脱盲,這里先不用ViewModel邑滨,后面再補(bǔ)充說明如何跟ViewModel一起使用。直接看例子:

public class TestModel {
    private MutableLiveData<String> status;

    public MutableLiveData<String> getStatus() {
        if (status == null)
            status = new MutableLiveData<>();
        return status;
    }
}

MutableLiveData繼承自LiveData钱反,表示可變數(shù)據(jù)掖看。這里創(chuàng)建一個保存String類型數(shù)據(jù)的LiveData

3.3 觀察LiveData對象

通常面哥,會在ActivityonCreate()方法中開始對LiveData的觀察:

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestModel mTestModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        //創(chuàng)建一個觀察者去更新UI
        final Observer<String> statusObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };
        //創(chuàng)建TestModel對象
        mTestModel = new TestModel();
        //觀察LiveData對象
        //這里的this指的是LifecycleOwner哎壳,即LiveData會跟生命周期聯(lián)系起來
        mTestModel.getStatus().observe(this, statusObserver);
    }
}

觀察者可以通過observe()方法對LiveData進(jìn)行監(jiān)聽,observe()方法需要傳遞一個LifecycleOwner參數(shù)進(jìn)去尚卫,這表示LiveData會跟生命周期聯(lián)系起來归榕。

另外還有一個observeForever()方法,只需傳遞一個觀察者進(jìn)去就行吱涉,這意味著它跟生命周期沒有任何關(guān)系刹泄,可以持續(xù)的觀察,只要數(shù)據(jù)發(fā)生變化怎爵,都會通知觀察者回調(diào)onChanged()循签。

3.4 更新LiveData對象

MutableLiveData提供了setValue(T)(主線程使用)和postValue(T)(子線程使用)兩個方法來修改數(shù)據(jù)。LiveData并沒有提供類似方法疙咸。當(dāng)調(diào)用MutableLiveDatasetValue(T)postValue(T)方法后,ObserveronChanged()方法將會被回調(diào)风科,從而實(shí)現(xiàn)更新UI的操作撒轮。

注意乞旦,這是在觀察者處于STARTEDRESUMED狀態(tài)時,LiveData才會通知觀察者數(shù)據(jù)變化题山;當(dāng)觀察者處于其他狀態(tài)時兰粉,即使LiveData的數(shù)據(jù)變化了,也不會通知顶瞳。

當(dāng)組件的狀態(tài)為DESTROYED時會自動移除觀察者玖姑,這樣ActivityFragment就可以安全地觀察LiveData而不用擔(dān)心造成內(nèi)存泄露。

我們來看個完整的例子:

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestModel mTestModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        //創(chuàng)建一個觀察者去更新UI
        final Observer<String> statusObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };
        //創(chuàng)建TestModel對象
        mTestModel = new TestModel();
        //觀察LiveData對象
        //這里的this指的是LifecycleOwner慨菱,即LiveData會跟生命周期聯(lián)系起來
        mTestModel.getStatus().observe(this, statusObserver);

        mTestModel.getStatus().setValue("onCreate");
    }

    @Override
    protected void onStart() {
        super.onStart();
        mTestModel.getStatus().setValue("onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        mTestModel.getStatus().setValue("onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        mTestModel.getStatus().setValue("onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        mTestModel.getStatus().setValue("onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mTestModel.getStatus().setValue("onDestroy");
    }
}

一個完整的生命周期走下來焰络,其輸出結(jié)果為:

07-26 22:03:34.798 20995-20995/com.test.start D/test: onChanged: onStart
07-26 22:03:34.801 20995-20995/com.test.start D/test: onChanged: onResume
07-26 22:03:36.456 20995-20995/com.test.start D/test: onChanged: onPause

可以看到,只在onStart()符喝、onResume()闪彼、onPause()時觀察者才會收到數(shù)據(jù)更新的通知,其他狀態(tài)下即使更新了數(shù)據(jù)也不會收到通知协饲。

3. LiveData配合ViewModel使用

下面來看下跟ViewModel是如何一起使用的畏腕。

3.1 添加依賴

使用ViewModel還需添加ViewModel的依賴:

dependencies {
    //...
    def lifecycle_version = "1.1.1"
    //ViewModel 和 LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
}

3.2 創(chuàng)建ViewModel

public class TestViewModel extends ViewModel {
    private MutableLiveData<String> status;

    public MutableLiveData<String> getStatus() {
        if (status == null)
            status = new MutableLiveData<>();
        return status;
    }
}

繼承ViewModel后即可。

3.3 觀察并更新LiveData對象

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestViewModel mTestModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        final Observer<String> statusObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };

//        mTestModel = new TestModel();

        //改為通過ViewModel來創(chuàng)建對象
        mTestModel = ViewModelProviders.of(this).get(TestViewModel.class);
        mTestModel.getStatus().observe(this, statusObserver);
    }

    @Override
    protected void onResume() {
        super.onResume();
        //更新數(shù)據(jù)
        mTestModel.getStatus().setValue("onResume");
    }
}

ViewModel跟普通Model流程都是一樣的茉稠,只是創(chuàng)建對象時不一樣而已描馅。
Google建議LiveData配合ViewModel一起使用。

4. 擴(kuò)展LiveData

除了使用MutableLiveData外而线,我們還可以通過繼承LiveData類來擴(kuò)展一些功能铭污。
比如:只有觀察者觀察了LiveData的數(shù)據(jù)時,才開始進(jìn)行進(jìn)行獲取數(shù)據(jù)的操作吞获,這樣可以節(jié)省資源况凉,代碼如下所示:

4.1 繼承LiveData

public class TestLiveData extends LiveData<String> {
    private static TestLiveData sInstance;
    private LocationUtil mLocationUtil;

    //設(shè)計為單例模式
    public static TestLiveData getInstance() {
        if (sInstance == null) {
            sInstance = new TestLiveData();
        }
        return sInstance;
    }

    private TestLiveData() {
        //創(chuàng)建一個獲取位置的對象
        mLocationUtil = new LocationUtil();
    }

    @Override
    protected void onActive() {
        //開始獲取位置信息
        mLocationUtil.start(mLocationListener);
    }

    @Override
    protected void onInactive() {
        //停止獲取位置信息
        mLocationUtil.stop();
    }
    
    //創(chuàng)建一個位置監(jiān)聽器
    private LocationListener mLocationListener = new LocationListener() {
        @Override
        public void onReceiveLocation(String location) {
            //接受到位置信息后,更新數(shù)據(jù)
            setValue(location);
        }
    };
}

LiveData設(shè)計成單例模式后各拷,可以在多個Activity刁绒、FragmentService之間共享它。

下面我們來重點(diǎn)關(guān)系一下LiveData的三個方法:onActive()烤黍、onInactive()知市、setValue()

  • onActive():當(dāng)有一個處于活躍狀態(tài)的觀察者監(jiān)聽LiveData時會被調(diào)用速蕊,這表示開始獲取位置信息嫂丙。
  • onInactive():當(dāng)沒有任何處于活躍狀態(tài)的觀察者監(jiān)聽LiveData時會被調(diào)用。由于沒有觀察者在監(jiān)聽了规哲,所以也沒必要繼續(xù)去獲取位置信息了跟啤,這只會消耗更多的電量等等,因此就可以停止獲取位置信息了。
  • setValue():更新LiveData的值隅肥,并通知到觀察者竿奏。

4.2 使用自定義的LiveData

下面來看下怎么使用這個自定義的LiveData

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestLiveData mTestLiveData;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        final Observer<String> statusObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };
        //通過單例獲取對象
        mTestLiveData =TestLiveData.getInstance();
        //同樣是去觀察LiveData
        mTestLiveData.observe(this, statusObserver);
    }
}

跟之前的一樣,還是通過一個觀察者去監(jiān)聽這個mTestLiveData對象腥放。

5. LiveData變換

LiveData變換主要有兩種變換:mapswitchMap泛啸,都是Transformations類提供的。

5.1 map變換

public class TestViewModel extends ViewModel {
    private MutableLiveData<Integer> mNumLiveData= new MutableLiveData<>();
    
    //通過Transformations.map()將Integer類型的值轉(zhuǎn)換為String類型
    private LiveData<String> mStrLiveData = Transformations.map(mNumLiveData, num -> num + "");

    public MutableLiveData<Integer> getNumLiveData() {
        return mNumLiveData;
    }
    
    public LiveData<String> getStrLiveData() {
        return mStrLiveData;
    }
}

map變換可以直接修改返回的值和類型秃症。

5.2 switchMap變換

public class TestViewModel extends ViewModel {

    private MutableLiveData<Integer> mNumLiveData = new MutableLiveData<>();
    
    //switchMap變換
    private LiveData<String> mNameLiveData = Transformations.switchMap(mNumLiveData, num -> getName(num));
    
    //返回一個LiveData
    private LiveData<String> getName(Integer num) {
        MutableLiveData<String> liveData = new MutableLiveData<>();
        liveData.setValue(num + "a");
        return liveData;
    }

    public MutableLiveData<Integer> getNumLiveData() {
        return mNumLiveData;
    }

    public LiveData<String> getNameLiveData() {
        return mNameLiveData;
    }
    
}

switchMap變換需要返回一個LiveData對象候址,這就是跟map變換的區(qū)別。

6. 合并多個LiveData數(shù)據(jù)源

如果有多個LiveData种柑,可以使用MediatorLiveData來合并這些LiveData岗仑,一旦其中一個LiveData發(fā)生變化,MediatorLiveData都會通知觀察者莹规。比如:一個UI界面赔蒲,依賴于網(wǎng)絡(luò)數(shù)據(jù)和數(shù)據(jù)庫,因此就會存在兩個LiveData良漱。使用MediatorLiveData將兩個LiveData合并后舞虱,UI界面只需要觀察一個MediatorLiveData即可。當(dāng)其中一個LiveData數(shù)據(jù)發(fā)生變化時都會通知UI界面去更新母市。

6.1 MediatorLiveData使用例子

MediatorLiveData的簡單使用如下所示:

        LiveData liveData1 = ...;
        LiveData liveData2 = ...;

        MediatorLiveData liveDataMerger = new MediatorLiveData<>();

        liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
        liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));

然后在UI界面直接觀察這個liveDataMerger即可矾兜。

6.2 MediatorLiveData的方法

MediatorLiveData相比于LiveData,主要是多了以下兩個方法:

  • addSource():添加源LiveData患久,并且開始監(jiān)聽給定的源LiveData椅寺,當(dāng)源LiveData的數(shù)據(jù)發(fā)生變化時,觀察者的onChanged()方法將會被調(diào)用蒋失,前提是MediatorLiveData處于活躍狀態(tài)返帕。
  • removeSource():移除LiveData

再來看個例子:

liveDataMerger.addSource(liveData1, new Observer() {
      private int count = 1;

      @Override public void onChanged(@Nullable Integer s) {
          count++;
          liveDataMerger.setValue(s);
          if (count > 10) {
              liveDataMerger.removeSource(liveData1);
          }
      }
 });

監(jiān)聽liveData1的數(shù)據(jù)篙挽,當(dāng)監(jiān)聽了10次之后荆萤,移除對liveData1監(jiān)聽。

6.3 使用Transformations實(shí)現(xiàn)自定義變換

MediatorLiveData除了可以合并數(shù)據(jù)外铣卡,實(shí)際上還可以用來實(shí)現(xiàn)自定義的變換方法链韭,上面Transformationsmap()switchMap()如果不能滿足變換需求的話,那么就可以用MediatorLiveData來實(shí)現(xiàn)煮落。

7. LiveData優(yōu)點(diǎn)

使用LiveData具有以下優(yōu)點(diǎn):

  • 1.確保UI跟數(shù)據(jù)狀態(tài)一致
    組件處于活躍狀態(tài)時敞峭,當(dāng)數(shù)據(jù)發(fā)生變化,LiveData會通知觀察者去更新UI蝉仇,從而使得他們保持一致旋讹。

  • 2.沒有內(nèi)存泄露
    由于觀察者綁定到了Lifecycle對象上殖蚕,因此在Lifecycle被銷毀后,觀察者會被自行清理掉骗村。

  • 3.停止Activity不會造成崩潰
    如果Activity處于非活躍的狀態(tài)嫌褪,比如Activity在后臺時,那么它不會接受到LiveData的數(shù)據(jù)變更事件胚股。

  • 4.無需手動處理生命周期
    UI組件僅僅需要觀察相應(yīng)的數(shù)據(jù)即可,無需手動去停止或恢復(fù)觀察裙秋。因?yàn)?code>LiveData會自動管理這所有的琅拌,它在觀察時能夠意識到相關(guān)的生命周期狀態(tài)變化。

  • 5.始終保持最新數(shù)據(jù)
    如果生命周期處于不活躍的狀態(tài)摘刑,那么當(dāng)它變?yōu)榛钴S狀態(tài)時將會收到最新的數(shù)據(jù)进宝。比如:后臺Activity變?yōu)榍芭_時將會收到最新的數(shù)據(jù)。

  • 6.適當(dāng)?shù)呐渲酶?br> 如果由于配置更改而重新去創(chuàng)建ActivityFragment枷恕,那么會立即接收最新的可用數(shù)據(jù)党晋。

  • 7.資源共享
    你可以繼承LiveData并使用單例模式來擴(kuò)展系統(tǒng)的服務(wù),這樣你就可以共享它徐块。這個自定義的LiveData對象只需連接一次系統(tǒng)服務(wù)未玻,其他需要這些資源的觀察者只需觀察這個LiveData即可『兀可以參看第4小節(jié):擴(kuò)展LiveData扳剿。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市昼激,隨后出現(xiàn)的幾起案子庇绽,更是在濱河造成了極大的恐慌,老刑警劉巖橙困,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞧掺,死亡現(xiàn)場離奇詭異,居然都是意外死亡凡傅,警方通過查閱死者的電腦和手機(jī)辟狈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來像捶,“玉大人上陕,你說我怎么就攤上這事⊥卮海” “怎么了释簿?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長硼莽。 經(jīng)常有香客問我庶溶,道長煮纵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任偏螺,我火速辦了婚禮行疏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘套像。我一直安慰自己酿联,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布夺巩。 她就那樣靜靜地躺著贞让,像睡著了一般。 火紅的嫁衣襯著肌膚如雪柳譬。 梳的紋絲不亂的頭發(fā)上喳张,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音美澳,去河邊找鬼销部。 笑死,一個胖子當(dāng)著我的面吹牛制跟,可吹牛的內(nèi)容都是我干的舅桩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼凫岖,長吁一口氣:“原來是場噩夢啊……” “哼江咳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起哥放,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤歼指,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡互艾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了挟阻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡峭弟,死狀恐怖附鸽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瞒瘸,我是刑警寧澤坷备,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站情臭,受9級特大地震影響省撑,放射性物質(zhì)發(fā)生泄漏赌蔑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一竟秫、第九天 我趴在偏房一處隱蔽的房頂上張望娃惯。 院中可真熱鬧,春花似錦肥败、人聲如沸趾浅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽潮孽。三九已至,卻和暖如春筷黔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仗颈。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工佛舱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挨决。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓请祖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親脖祈。 傳聞我的和親對象是個殘疾皇子肆捕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 懶得處理樣式了, 將就著看吧. 官網(wǎng)地址: https://developer.android.com/topic...
    Reddington_604e閱讀 1,658評論 0 1
  • 前言 Android Architecture Components,簡稱 AAC 盖高,是 Google IO 20...
    前行的烏龜閱讀 5,773評論 3 27
  • 前言: 作為一名移動互聯(lián)網(wǎng)App研發(fā)人員慎陵,在實(shí)際項(xiàng)目的研發(fā)過程中,保質(zhì)保量高效率喻奥,方便快捷席纽,同時方便開發(fā)者之間的互...
    Yagami3zZ閱讀 4,656評論 1 9
  • 示例應(yīng)用程序 使用LiveData的優(yōu)點(diǎn) 使用LiveData對象創(chuàng)建LiveData對象觀察LiveData對象...
    yyg閱讀 5,857評論 5 7
  • 錯誤處理(Error handling)是響應(yīng)錯誤以及從錯誤中恢復(fù)的過程。Swift 提供了在運(yùn)行時對可恢復(fù)錯誤的...
    勇往直前888閱讀 1,024評論 0 0