感謝你的再次光臨,歡迎來到Android Architecture Components(ACC)系列文章。上篇文章我們一起討論了Room,通過Room我們能夠方便的操作App的資料庫。如果你的App對本地資料庫有所依賴的話,Room你值得擁有。
今天這篇文章繼續上篇文章的步伐,讓我們一起來全面瞭解ACC另一強大的元件LiveData。相信你馬上會喜歡上她!???
簡述
LiveData是一種可觀測資料容器,它會在資料變化時通知觀測器,以便更新頁面;同時它具備生命感知能力,可以實時觀察Activity/Fragment的生命週期狀態。
既然它是可觀察資料容器與具備生命感知能力,那麼它的優點也很明顯,可以歸納與以下幾點
- 確保ui跟隨資料更新
- 具備生命感知能力從而減少記憶體洩露
- 防止異常crashs
- 無需管理繫結者的生命週期
- ui獲取的資料都是最近最終的更新資料
使用場景
當我們要監聽某一個資料的變化時,LiveData將大顯身手。例如介面資料的更新,當資料發生變化時,我們要通知介面進行更新ui,這時我們可以使用LiveData在當前Activity/Fragment中對該資料註冊一個觀察者,實時監聽資料的任何改動。每一次改動LiveData都會傳送通知給觀察者。
另一方面,LiveData感知介面的生命週期,所以只有在介面生命週期的STARTED或者RESUMED狀態才會通知觀察者。如果你一直處於後臺且資料一直在變化,LiveData是不會發生通知,只有在介面再一次回到前臺,這時LiveData才會發生通知且只會傳送一次,資料的更新取的是最後一次的變化資料。這樣可以有效的避免記憶體洩露與ui不存在時導致的NullPointerException
使用
首頁我們需要在我們的app下的build.gradle中新增如下依賴程式碼
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,看如下程式碼:
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)來設定值與傳送通知,告訴觀察者資料已經改變。
在**getDataFromLocal()**方法中,我們使用了Room來運算元據庫,同時直接通過返回LiveData資料型別的資料,使得Room與LiveData完美結合。
所以我們再來看看觀察者的程式碼:
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