放弃官方,拥抱自我,自定义 LiveData 才是最合适的

栏目: Android · 发布时间: 7年前

内容简介:标题起的有点大啊,不要在意我想我不是应该不是第一个自己写 LiveData 的,18年初开始使用 AAC 的这些组件,我觉得 viewModule 不实用,拿 viewModule 当 P 用不是能很好满足需求,LiveData 用了一段时间,尼玛坑死我了,反倒是让我费了不少事,死来想起还是自己写一个 LiveData 吧,自己写的才是最合适自己的AAC 组件里最有意义的我认为是 LiveData , LiveData 在继 RX 之后深刻的告诉了我们数据流的概念,让我们看到了数据和流最佳的结合方式,这种变

标题起的有点大啊,不要在意

放弃官方,拥抱自我,自定义 LiveData 才是最合适的

我想我不是应该不是第一个自己写 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 抽象基类,具体实现,为啥这么麻烦呢,一是为了练手培养代码规范,二是这样设计方便扩展不是

放弃官方,拥抱自我,自定义 LiveData 才是最合适的
  • 根接口 - 就是设置,获取,发射数据,使用泛型接受数据类型
/**
 * 作者 : 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")
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learn Python the Hard Way

Learn Python the Hard Way

Zed Shaw / Example Product Manufacturer / 2011

This is a very beginner book for people who want to learn to code. If you can already code then the book will probably drive you insane. It's intended for people who have no coding chops to build up t......一起来看看 《Learn Python the Hard Way》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具