標題起的有點大啊,不要在意
我想我不是應該不是第一個自己寫 LiveData 的,18年初開始使用 AAC 的這些組件,我覺得 viewModule 不實用,拿 viewModule 當 P 用不是能很好滿足需求,LiveData 用了一段時間,尼瑪坑死我了,反倒是讓我費了不少事,死來想起還是自己寫一個 LiveData 吧,自己寫的纔是最合適自己的
AAC 組件裏最有意義的我認爲是 LiveData , LiveData 在繼 RX 之後深刻的告訴了我們數據流的概念,讓我們看到了數據和流最佳的結合方式,這種變化是非常適應時下發展潮流的,但是奈何 LiveData 有些坑讓我棄用他了
LiveData 的坑: - 我就不上代碼了,用過的都知道
- 必須在主線程發送數據
- 註冊觀察者必須要傳 Lifecycle,有時候我們並不是都在頁面級別使用的,比如說拿 LiveData 當 RXbus 用,雖然有不用傳 Lifecycle 的方法 observeForever,但是穿與不穿 Lifecycle 居然是2個 api 我覺得用着不爽
- LiveData 最大的問題,同時也是我最不能接受的:LiveData 無法判斷值是不是新值,只要 LiveData 設置過數據了,那麼不管你是不是第一次註冊觀察者或者頁面重新激活顯示時,都會收到消息沒,這就不能忍了,這不就是亂髮射數據嘛,完全不能準確的表達我發射數據的動作,老的數據也發,很多時候我並不需要你現在的數據,這讓我很爲難,爲了處理這個問題,浪費了不少精力,反倒是麻煩了,就像我討厭有的 Google API 的命名,什麼垃圾玩意,什麼名都敢起,有的時候查字典看翻譯我都不知道是幹嘛的
自定義 LiveData 簡單思路
其實自己寫個 LiveData 出來非常簡單,easy+輕鬆,核心就是用 PublishSubject 做熱發射,熱發射不熟悉的朋友可以看我的文章:我學 rxjava 2(3)- 熱發射
在註冊的時候用戶要是傳 Lifecycle ,那麼就在 Lifecycle 身上註冊一個觀察者,頁面 onDes 關閉時使用管道解綁,同時一提供一個 map 保存管道,用於用戶自行解綁
什麼時候響應數據這是用戶的自己問題,用戶自行判斷要不要激活操作,我們都來到 Lifecycle 的時代了, Lifecycle 自身就提供了頁面狀態的 API,判斷起來也不麻煩
// 獲取頁面狀態
lifecycle.currentState
// lifecycle 類裏有提供狀態的枚舉
public enum State {
DESTROYED,
INITIALIZED,
CREATED,
STARTED,
RESUMED;
}
LiveData 提供變換,但是變換隻能變換一個,並且變換得到的 LiveData 不能發射數據,必要使用原始的 LiveData 才行,並且變換 API 不是在 LiveData 身上的,而是一個輔助類,這就用着很不爽了,我們都熟悉了 Rxjava 這麼久了,不是鏈式的 API 我們都 diss 他,這點 Google 有點落後了
// Transformations.map()
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
// Transformations.switchMap()
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
這裏針對變換的問題,我覺得既然 Rxjava 已經實現的很好了,何必在多一手呢,再說再怎麼寫也肯定不如 rx 不是,所以提供一個方法直接把 subject 拋出來,缺點是就沒有自動解綁的功能了,優點是不影響我們發送數據,我們還是用的 subject 發射數據,我測試過了沒問題的
自定義 LiveData 代碼
爲了結構規整,我設計了3層 API,根接口,abs 抽象基類,具體實現,爲啥這麼麻煩呢,一是爲了練手培養代碼規範,二是這樣設計方便擴展不是
- 根接口 - 就是設置,獲取,發射數據,使用泛型接受數據類型
/**
* 作者 : BloodCrown
* 時間 : 2019-05-05 16:03
* 描述 : 自定義 LiveData 跟接口
*
* 1. 提供獲取設置數據的接口
* 2. 發送數據的接口
*/
interface IMyLiveData<T> {
/**
* 獲取數據
*/
fun getValue(): T?
/**
* 設置數據
*/
fun setValue(t: T)
/**
* 發送
*/
fun sendValue(t: T)
}
- abs 抽象基類 - 填充數據對象
/**
* 作者 : BloodCrown
* 時間 : 2019-05-05 16:09
* 描述 : 自定義 LiveData 的抽象基類
*
* 1. 實現根接口,提供數據存儲,獲取功能
* 2. 發送數據應該是具體實現關心的
*
*/
abstract class AbsMyLiveData<T> : IMyLiveData<T> {
// 數據對象
private var mValue: T? = null
override fun getValue(): T? {
return mValue
}
override fun setValue(t: T) {
this.mValue = t
}
}
- 具體實現 - 沒啥說的,很簡單,看就完了,沒有看不懂的,看不懂的喊我,我立馬機票飛你那去...
/**
* 作者 : BloodCrown
* 時間 : 2019-05-05 15:58
* 描述 :
* 1. 自定義的 LiveData,爲了是去掉 LiveData 一些不合時宜的設定
* 2. 自己寫的才能百分百按照自己的設想去做
*
* 成員變量描述:
* 1. subject 對外提供的 PublishSubject 用於熱發射
* 2. disposableList map 集合,用來存儲管道對象,因爲有的訂閱沒有頁面級別的生命週期
*
* 功能:
* 1. sendValue 發送數據
* 2. addObserver 註冊觀察者
* lifecycle != null -> 會在註冊觀察者的同時,在 Lifecycle.Event.ON_DESTROY 時會解除綁定
* tag != null -> 會把管道對象保存到 map 集合裏,用於自助解除註冊
*/
class MyLiveData<T> : AbsMyLiveData<T>() {
// 核心數據數據被觀察者
var subject = PublishSubject.create<T>()
// 保存管道的 map 集合
var disposableList: MutableMap<String, Disposable> = mutableMapOf()
/**
* 發送數據
*/
override fun sendValue(data: T) {
if (data == null) return
setValue(data)
subject.onNext(data)
}
/**
* 註冊觀察者,考慮了沒有頁面級別的生命週期的情況
*
* lifecycle != null -> 會在註冊觀察者的同時,在 Lifecycle.Event.ON_DESTROY 時會解除綁定
* tag != null -> 會把管道對象保存到 map 集合裏,用於自助解除註冊
*/
fun addObserver(tag: String? = null, lifecycle: Lifecycle? = null, observer: (data: T) -> Unit) {
var disposable = subject.subscribe {
observer(it)
}
if (tag != null) disposableList.put(tag, disposable)
if (disposable != null) lifecycle?.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun destroy() {
if (!disposable.isDisposed) disposable.dispose()
disposableList.remove(tag)
if (tag != null) disposableList.remove(tag)
}
})
}
/**
* 手動解除註冊,只適用於在註冊時沒有傳入 lifecycle 的朋友
*/
fun removeOberver(tag: String) {
if (tag == null) return
var disposable = disposableList.get(tag)
if (disposable == null) return
if (!disposable?.isDisposed) disposable?.dispose()
disposableList.remove(tag)
}
/**
* 用於用戶自行變換擴展,不過這樣就不能自行解綁了,需要用戶手動進行解綁操作
*/
fun getObservable(): PublishSubject<T> {
return subject
}
}
// 創建 MyLiveData 對象
var liveData = MyLiveData<String>()
// 註冊多個監視器
liveData.addObserver("AA", this.lifecycle) {
Log.d("AA", "MyLiveData 接受到數據11: $it")
}
liveData.addObserver("AA", this.lifecycle) {
Log.d("AA", "MyLiveData 接受到數據22: $it")
}
// 發射數據
liveData.sendValue("AA")
// 手動解綁
liveData.removeOberver("AA")
最後
最後沒啥說的了,由需求大家自己擴展吧,