感謝你的再次光臨,歡迎來到Android Architecture Components(ACC)系列文章。上篇文章咱們一塊兒討論了Room,經過Room咱們可以方便的操做App的數據庫。若是你的App對本地數據庫有所依賴的話,Room你值得擁有。java
今天這篇文章繼續上篇文章的步伐,讓咱們一塊兒來全面瞭解ACC另外一強大的組件LiveData。相信你立刻會喜歡上她!😍😍😍android
LiveData是一種可觀測數據容器,它會在數據變化時通知觀測器,以便更新頁面;同時它具有生命感知能力,能夠實時觀察Activity/Fragment的生命週期狀態。git
既然它是可觀察數據容器與具有生命感知能力,那麼它的優勢也很明顯,能夠概括與如下幾點github
當咱們要監聽某一個數據的變化時,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
咱們已經知道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來全局監聽參數的傳遞與變化。
在通知觀察者數據改變以前,若是你想改變LiveData中的值類型,可使用Transformations
獲取原有類型中的某個特定的類型值,能夠比喻爲解包,可使用map()方法
LiveData<User> userLiveData = ...; LiveData<String> userName = Transformations.map(userLiveData, user -> { user.name + " " + user.lastName });
與map對應的是switchMap()方法,這裏就是打包。
private LiveData<User> getUser(String id) { ...; } LiveData<String> userId = ...; LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
與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