標題起的有點大啊,不要在意
我想我不是應該不是第一個自己寫 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")
複製程式碼
最後
最後沒啥說的了,由需求大家自己擴充套件吧,