Android Architecture Components Part2:LiveData

clipboard.png

感謝你的再次光臨,歡迎來到Android Architecture Components(ACC)系列文章。上篇文章咱們一塊兒討論了Room,經過Room咱們可以方便的操做App的數據庫。若是你的App對本地數據庫有所依賴的話,Room你值得擁有。java

今天這篇文章繼續上篇文章的步伐,讓咱們一塊兒來全面瞭解ACC另外一強大的組件LiveData。相信你立刻會喜歡上她!😍😍😍android

簡述

LiveData是一種可觀測數據容器,它會在數據變化時通知觀測器,以便更新頁面;同時它具有生命感知能力,能夠實時觀察Activity/Fragment的生命週期狀態。git

既然它是可觀察數據容器與具有生命感知能力,那麼它的優勢也很明顯,能夠概括與如下幾點github

  1. 確保ui跟隨數據更新
  2. 具有生命感知能力從而減小內存泄露
  3. 防止異常crashs
  4. 無需管理綁定者的生命週期
  5. ui獲取的數據都是最近最終的更新數據

使用場景

當咱們要監聽某一個數據的變化時,LiveData將大顯身手。例如界面數據的更新,當數據發生變化時,咱們要通知界面進行更新ui,這時咱們可使用LiveData在當前Activity/Fragment中對該數據註冊一個觀察者,實時監聽數據的任何改動。每一次改動LiveData都會發送通知給觀察者。數據庫

另外一方面,LiveData感知界面的生命週期,因此只有在界面生命週期的STARTED或者RESUMED狀態纔會通知觀察者。若是你一直處於後臺且數據一直在變化,LiveData是不會發生通知,只有在界面再一次回到前臺,這時LiveData纔會發生通知且只會發送一次,數據的更新取的是最後一次的變化數據。這樣能夠有效的避免內存泄露與ui不存在時致使的NullPointerExceptionsegmentfault

使用

首頁咱們須要在咱們的app下的build.gradle中添加以下依賴代碼api

dependencies {
    def lifecycle_version = "1.1.1"
 
    // ViewModel and LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    // alternatively - just LiveData
    implementation "android.arch.lifecycle:livedata:$lifecycle_version"
 
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"
}

而後咱們就能正式使用LiveData,看以下代碼:app

class ContactsViewModel(application: Application, private val defTitle: String = "Contacts") : AndroidViewModel(application) {
    val message: MutableLiveData<String> by lazy { MutableLiveData<String>() }
    val contactsList: MutableLiveData<List<ContactsModel>> = MutableLiveData()
     
   fun getContacts(refresh: Boolean): LiveData<List<ContactsModel>> {
        message.value = ""
        if (refresh) {
            getDataFromRemote()
        } else if (contactsList.value == null || contactsList.value?.size ?: 0 <= 0) {
            message.value = "數據請求中,請稍後!"
            if (mLocalData.isEmpty()) {
                getDataFromLocal()
            }
        }
        return contactsList
    }
     
    private fun getDataFromLocal() {
        val runnable = Runnable {
            val dao = mContactsDao.getAllContacts()
            if (dao.isNotEmpty()) {
                contactsList.postValue(dao)
            } else {
                getDataFromRemote()
            }
        }
        mExecutors.disIoExecutor.execute(runnable)
    }
 
    private fun getDataFromRemote() {
        Handler().postDelayed({
            contactsList.value = mRemoteData
            mLocalData = mRemoteData
            saveContacts(mRemoteData)
            Thread(Runnable {
                title.postValue("Remote Contacts")
            }).start()
            message.value = "數據加載完成~"
        }, MDELAY_MILLIS)
    }
}

首先咱們使用MutableLiveDat對咱們所須要的數據進行了包裹,MutableLiveData它繼承與LiveData,暴露了postValue()setValue()方法。一旦MutableLiveData所包裹的數據發生變化,咱們能夠經過postValue()(asynchronously)與setValue()(synchronously)來設置值與發送通知,告訴觀察者數據已經改變。async

getDataFromLocal()方法中,咱們使用了Room來操做數據庫,同時直接經過返回LiveData數據類型的數據,使得Room與LiveData完美結合。

因此咱們再來看看觀察者的代碼:ide

class ContactsActivity : AppCompatActivity() {
 
    private lateinit var mViewModel: ContactsViewModel
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_contacts_layout)
        setupViewModel()
    }
    
    private fun setupViewModel() {
        mViewModel = ViewModelProviders.of(this, ContactsFactory.getInstance(application))[ContactsViewModel::class.java]
        //active STARTED、RESUMED
        mViewModel.getContacts(true).observe(this,
                Observer {
                    //todo ...
                })
        mViewModel.message.observe(this,
                Observer {
                    //todo ...
                })
    }   
}

咱們爲所須要觀察的數據添加了observer方法,該方法第一個參數是LifecyleOwner,以便讓LiveData具備生命感知能力,這裏要感知的是ContactsActivity,因此傳入this便可。第二個參數是一個回調方法,一旦數據發生變化它的onChanged()就會回調,並將數據帶回,這樣界面就能實時更新數據。

最後因爲LiveData是生命感知的因此咱們也無需擔憂他的register/unregister

Extend

咱們已經知道LiveData會對處於STATERD或者RESUMED狀態進行發送通知,若是該狀態下存在observer,由無到有,咱們稱之爲active,反正稱之爲inactive。若是咱們可以知道什麼時候爲active與什麼時候爲inactive,那麼咱們就能夠實現本身的LiveData。爲了解決這個問題,LiveData提供了兩個方法,分別爲onActive()onInactive()

例如咱們想爲一個監聽器實現生命感知能力,能夠進行以下操做

public class StockLiveData extends LiveData<BigDecimal> {
    private static StockLiveData sInstance;
    private StockManager mStockManager;
 
    private SimplePriceListener mListener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };
 
    @MainThread
    public static StockLiveData get(String symbol) {
        if (sInstance == null) {
            sInstance = new StockLiveData(symbol);
        }
        return sInstance;
    }
 
    private StockLiveData(String symbol) {
        mStockManager = new StockManager(symbol);
    }
 
    @Override
    protected void onActive() {
        mStockManager.requestPriceUpdates(mListener);
    }
 
    @Override
    protected void onInactive() {
        mStockManager.removeUpdates(mListener);
    }
}

一旦observer由無到有,那麼咱們就在onActive()方法中進行監聽器的註冊。observer由有到無,咱們能夠在onInactive()中進行註銷。這樣就能夠是咱們的監聽器具有生命感知能力。避免沒必要要的內存泄露或者一次crash。同時一旦監聽器的回調方法生效時,咱們又能夠經過LiveData的setValue()來對觀察者進行數據的更新。因此觀察者的代碼以下:

public class MyFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        StockLiveData.get(getActivity()).observe(this, price -> {
            // Update the UI.
        });
    }
}

若是細心的話,能夠發現上面的StockLiveData已經實現了監聽器共享。咱們能夠在多個界面中使用StockLiveData進行添加observer。例如在Activity中,只要有一個observer,那麼它將一直監聽數據的變化。

案例:對於App統計需求,一旦涉及到多個頁面間的統計參數傳遞,能夠自定義一個擴展LiveData來全局監聽參數的傳遞與變化。

Transform

在通知觀察者數據改變以前,若是你想改變LiveData中的值類型,可使用Transformations

Transformations.map()

獲取原有類型中的某個特定的類型值,能夠比喻爲解包,可使用map()方法

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});

Transformations.switchMap()

與map對應的是switchMap()方法,這裏就是打包。

private LiveData<User> getUser(String id) {
  ...;
}
 
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

MediatorLiveData

與LiveData相關的還有一個MediatorLiveData,它的做用是:能夠同時監聽多個LiveData。例如同時監聽本地數據與遠程數據。

LiveData<List<User>> usersFromDatabase;
LiveData<List<User>> usersFromNetwork;
 
MediatorLiveData<List<User>> usersLiveData = 
    new MediatorLiveData<>();
     
usersLiveData.addSource(usersFromDatabase, newUserList ->
    usersLiveData.setValue(value));
     
usersLiveData.addSource(usersFromNetwork, newUserList ->
    usersLiveData.setValue(value));

一旦其中一個發送變化,MediatorLiveData都會發送通知給observer。

是否感受LiveData很強大呢?那麼趕忙行動起來吧,讓你的App中數據也具備可觀察與生命感知能力。

最後文章中的代碼均可以在Github中獲取到。使用時請將分支切換到feat_architecture_components

相關文章

Android Architecture Components Part1:Room
Android Architecture Components Part3:Lifecycle
Android Architecture Components Part4:ViewModel

關注

私人博客

clipboard.png

相關文章
相關標籤/搜索