自定义 LiveData
2019-05-05 本文已影响29人
前行的乌龟
标题起的不太好,改了
好白...
我不是说 LiveData 不好,之前的描述被喷了,赶紧改了,LiveData API 有些地方不符合我的设计思路
- 发射数据要区分是不是主线程,setValue 在主线发射数据, postValue 在其他线程发射数据,我不喜欢这种 api 设计,使用起来不是很友好,增加了用户的学习成本
- 注册观察者的方法 Lifecycle 必须要传,不能传 null,这样就把一个方法割裂成了2个方法:observe、observeForever,没必要,判断 Lifecycle 是不是 null 走不同的逻辑就不行吗
- 我的场景里首次注册观察者时需不需要获取数据这样应该是我自己的问题,livedata 根据数据的 version 在首次注册时只要有数据就会发一次,我觉得这样逾越单一职能了, livedata 提供了 getValue 的方法就没必要在首次注册时发送数据了,我要是关心的话自己 getValue 拿下数据好了
自定义 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 我们都已经不熟悉了
// 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()
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")
最后
最后没啥说的了,由需求大家自己扩展吧,