背景
在 App 中意蛀,存在這樣幾組數(shù)據(jù)阴挣,需要在所有 Activity 和 Fragment 間共享蚌斩,也可以被任意對象訪問惹盼,被訪問時(shí),如果數(shù)據(jù)沒有初始化惑灵,就會(huì)先進(jìn)行初始化,數(shù)據(jù)準(zhǔn)備完畢后再傳遞給訪問者眼耀。實(shí)現(xiàn)方式有多種英支,比如利用 RxJava 將各組數(shù)據(jù)設(shè)計(jì)成可觀察對象。這里我們采用了 LiveData 和 ViewModel 哮伟,因?yàn)樵?Android 平臺干花,它們有著自己獨(dú)特的優(yōu)勢。
LiveData
LiveData 是能感知生命周期的楞黄,它遵循宿主(如 Activity池凄,F(xiàn)ragment,Service)的生命周期鬼廓,僅會(huì)在宿主生命周期激活時(shí)肿仑,才會(huì)通知更新數(shù)據(jù),使用 LiveData 也有以下的優(yōu)勢:
- 能及時(shí)更新宿主數(shù)據(jù)狀態(tài)
- 沒有內(nèi)存泄漏
- 不會(huì)再有當(dāng)宿主處于 onStop 時(shí)更新數(shù)據(jù)可能造成 crash 的情況
- 無需手動(dòng)處理生命周期
- 始終保持?jǐn)?shù)據(jù)最新
...
LiveData 常用的有下面幾種方法:
getValue()
獲取當(dāng)前數(shù)據(jù)
observe(LifecycleOwner owner, Observer<T> observer)
在 Lifecycle 宿主中訂閱 LiveData
setValue(T value)
更新數(shù)據(jù),注意此方法只能在主線程調(diào)用
postValue(T value)
更新數(shù)據(jù)尤慰,此方法允許在異步線程中調(diào)用
ViewModel
ViewModel 能很好的為 Activity 和 Fragment 管理數(shù)據(jù)馏锡,將數(shù)據(jù)相關(guān)的操作與 UI 層分離。
...
實(shí)現(xiàn)
首先定義 Data伟端,能在應(yīng)用內(nèi)任意訪問的數(shù)據(jù)杯道。
interface Data {
fun isEmpty(): Boolean
fun refresh()
}
這里看到其中定義了兩個(gè)方法,isEmpty()
责蝠, 用來判斷該數(shù)據(jù)是否為空党巾,refresh()
,用來更新數(shù)據(jù)自身霜医,比如拉取遠(yuǎn)端的最新數(shù)據(jù)齿拂,或從數(shù)據(jù)庫讀取。
接下來以用戶數(shù)據(jù)為例支子,首先創(chuàng)建一個(gè) User 類來定義數(shù)據(jù)結(jié)構(gòu):
data class User(var userId: Int, var userName: String, var userEmail: String) {
fun isEmpty(): Boolean = userId.isNullOrBlank() && userName.isNullOrBlank() && userEmail.isNullOrBlank()
}
這里 User 類持有了用戶 Id, 用戶名和用戶郵箱创肥。接下來定義 UserData:
class UserData: LiveData<User>(), Data {
override fun isEmpty(): Boolean {
return value == null || value.isEmpty()
}
override fun refresh() {
//異步拉取最新數(shù)據(jù)
asyncLoadData(newData ->
//更新
postValue(newData)
)
}
}
在 asyncLoadData
拉取到數(shù)據(jù)后,調(diào)用 postValue
更新 UserData值朋。
下面我們設(shè)計(jì)一個(gè)數(shù)據(jù)中心叹侄,所有的 Data 都存儲(chǔ)在數(shù)據(jù)中心里:
object DataCentral {
private val safeDataTable = ConcurrentHashMap<String, Data>()
fun accessor() = this@DataCentral
fun <T: Data> query(clazz: Class<T>): T?{
val queryKey = clazz.simpleName
if (!safeDataTable.containsKey(queryKey) || safeDataTable[queryKey] == null){
try {
safeDataTable[queryKey] = clazz.newInstance()
}catch (e: InstantiationException){
//Data block init error
...
return null
}catch (e: IllegalAccessException){
//Data block init error
...
return null
}catch (e: ExceptionInInitializerError){
//Data block init error
...
return null
}catch (e: SecurityException){
//Data block init error
...
return null
}catch (e: Exception){
...
}
}
return safeDataTable[queryKey] as? T
}
}
從上面的代碼中,可以看到所有的 Data 其實(shí)存儲(chǔ)在了一個(gè) HashMap 里昨登,這里采用了 ConcurrentHashMap 以支持并發(fā)操作趾代。另外我們以具體 Data 類的類名來作為存儲(chǔ)的 Key,這樣避免在數(shù)據(jù)量大時(shí)丰辣,需要維護(hù)相當(dāng)數(shù)量的 key撒强。每一組數(shù)據(jù)也只會(huì)在首次被訪問時(shí),才會(huì)在數(shù)據(jù)中心創(chuàng)建笙什,創(chuàng)建的方式是通過無參構(gòu)造函數(shù)直接新建實(shí)例飘哨,所以在定義每個(gè) Data 時(shí),需要包含一個(gè)無參的構(gòu)造函數(shù)琐凭。
現(xiàn)在數(shù)據(jù)中心有了芽隆,我們還需要一個(gè)媒介,用來訪問數(shù)據(jù)中心统屈,同時(shí)承擔(dān)與數(shù)據(jù)中心交互以外的部分職責(zé)胚吁,就像一個(gè)代理。顯然該 ViewModel 出場了:
class DataCentralAgent: ViewModel() {
private val dataCentralAccessor = DataCentral.accessor()
fun <T: Data> get(clazz: Class<T>): T?{
return dataCentralAccessor.query(clazz)?.apply {
if (isEmpty()) {
refresh()
...
}
}
}
override fun onCleared() {
super.onCleared()
...
}
}
我們看到 get(clazz: Clazz<T>)
方法愁憔,傳入需要獲取的具體 Data 類型腕扶,數(shù)據(jù)中心會(huì)將 Data 查詢出來并返回,同時(shí)對數(shù)據(jù)進(jìn)行判空吨掌,如果為空半抱,便調(diào)用 refresh()
方法更新該數(shù)據(jù)的值脓恕。
那么在 Activity 和 Fragment 中,我們只需要通過 DataCentralAgent 來 get
我們需要的數(shù)據(jù)就可以了代虾,即使數(shù)據(jù)還沒有準(zhǔn)備好进肯,它也會(huì)自己默默在后臺更新自己,接著借助于 LiveData 的特性將更新好的數(shù)據(jù)傳遞給訂閱者棉磨。我們還可以寫一個(gè)輔助類江掩,來幫助快速創(chuàng)建 Agent:
object DataCentralAgentFactory {
@MainThread
fun createFor(activityOwner: FragmentActivity): DataCentralAgent =
ViewModelProviders.of(activityOwner).get(DataCentralAgent::class.java)
@MainThread
fun createFor(fragmentOwner: Fragment): DataCentralAgent =
ViewModelProviders.of(fragmentOwner).get(DataCentralAgent::class.java)
}
下面我們寫一個(gè)訪問示例:
class MyFragment: Fragment {
override fun onCreate(savedInstanceState: Bundle?) {
DataCentralAgentFactory
.createFor(this)
.get(UserData::class.java)
?.observe(this@MyFragment, object: Observer<User> {
override fun onChanged(user: User?) {
...
}
})
super.onCreate(savedInstanceState)
...
}
}
到此一個(gè)應(yīng)用內(nèi)數(shù)據(jù)中心就搭建完成了。
(轉(zhuǎn)載請注明出處)