Jetpack 学习二:LiveData组件

·  阅读 337

LiveData 组件

什么是LiveData

LiveData组件是Jetpack推出的基本观察者模式的消息订阅和分发组件,具有宿主(Activity/Fragment)生命感知能力,这种感知能力,可确保LiveData暂时只分发给处于活跃状态的观察者

活跃状态:通常情况下等于Observer处于started,resumed状态,当然如果注册观察者时使用observerForever时,则该Observer则一直处于活跃状态

LiveData只分发给处于活跃状态的Observer,既保证了活跃的Observer数据和界面及时更新,又省去了处在不活跃状态Observer在后台刷新数据和界面占用的资源. LiveData的出现解决了以往回调方式带来的空指针,后台抢占资源,手动反注册等问题

LiveData优势

  • 确保界面符合数据状态

    LiveData 遵循观察者模式。当生命周期状态发生变化时,LiveData 会通知 [Observer]对象并把最新数据派发给它。观察者可以在收到onChanged事件时更新界面,而不是在每次数据发生更改时立即更新界面。

  • 不再需要手动处理生命周期

    只需要观察相关数据,不用手动停止或恢复观察。LiveData 会自动管理Observer的反注册,因为它能感知宿主生命周期的变化,并在宿主生命周期的onDestory自动进行反注册。因此使用LiveData做消息分发不会发生内存泄漏

  • 数据始终保持最新状态

    如果宿主的生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。

  • 支持黏性事件的分发

    即先发送一条数据,后注册一个观察者,默认是能够收到之前发送的那条数据的

  • 共享资源

    我们可以使用单例模式拓展 LiveData,实现全局的消息分发总线。


LiveData的使用

简单使用

LiveData 是抽象类,它有一个简单的实现类MutableLiveData

class LiveDataActivity : AppCompatActivity() {
    private val myHandler = Handler()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)
        testLiveData1()
    }
	//创建LiveData对象
    private val myLiveData = MutableLiveData<String>()
    private fun testLiveData1() {
    	//添加观察者
        myLiveData.observe(this, {
            Log.i("LiveDataActivityTag",it)
        })
        //使用postValue 发送值
        myLiveData.postValue("My------------")
        myHandler.postDelayed({
        	//延迟3s后使用setValue设置值
            myLiveData.value = "My++++++++++++++++++"
        }, 3000)
    }
}
复制代码

结果(注意时间)

接下来测试一下不活跃状态下发送更改值的效果,打开界面后立马按下Home键停留超过3s后回来

结果(注意时间) 这也验证了LiveData只是暂时不发分给处于不活跃状态的Observer,当该Observer重新回到活跃状态时立马又给该Observer发送消息及时更新数据

LiveData.setValue() 该方法调用必须在主线程中

LiveData.postValue() 该方法可在子线程中调用也可在主线程调用

LiveData的操作符使用

Transformations.map()使用

如果想在LiveData分发数据之前对数据进行更改可使用该操作符

class LiveDataActivity : AppCompatActivity() {
    private val myHandler = Handler()
    private val myLiveData = MutableLiveData<String>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)
        testMap()
    }

    private fun testMap() {
        myLiveData.observe(this, Observer {
        	//用于观察元数据
            Log.i("LiveDataActivityTag", it)
        })
        //map转换之后会返回对应泛型的LiveData对象
        val map = Transformations.map(myLiveData, Function {
        	//对结果拼接了几个美元符号
            "$it$$$$$$$$$$"
        })
        //监听转换后的LiveData
        map.observe(this, Observer {
            Log.i("LiveDataActivityTag", it)
        })
        //发送值
        myLiveData.postValue("My")
        //延迟3s后发送另一个值
        myHandler.postDelayed({
            myLiveData.postValue("You")
        }, 3000)
    }
 }
复制代码

结果: 跟上面例子一样,在处于不活跃状态下也是不会收到数据的,这里不再演示

Transformations.switchMap()使用

当你需要根据某种条件切换观察不同的LiveData时可使用该操作符

class LiveDataActivity : AppCompatActivity() {
	//定义三个LiveData,其中myLiveData3作为切换条件
    private val myLiveData = MutableLiveData<String>()
    private val myLiveData2 = MutableLiveData<String>()
    private val myLiveData3 = MutableLiveData<Boolean>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)
        testSwitchMap()
    }

    /**
    kotlin接口写法总结:
    一个原则,三个优化:
    原则:
    接口作为参数时参数位置替换为此写法:{参数1,参数2 ... -> {需要运行的方法}}
    优化:
    1.当接口做参数位于最后一个参数时,可以提到小括号外面
    2.如果是唯一参数,甚至可以省略小括号
    3.当接口的回调方法参数只有一个时,参数和函数推导符可以省略,默认it为其参数
     * */
    private fun testSwitchMap() {
    	//以myLiveData3作为切换条件 进行不同的返回
        val switchMap = Transformations.switchMap(myLiveData3) { input ->
            if (input)
                myLiveData
            else
                myLiveData2
        }
        //观察该数据变化
        switchMap.observe(this, Observer {
            Log.i("LiveDataActivityTag", it)
        })
        //先发动切换条件
        myLiveData3.postValue(true)
        //两个LiveData分别发送数据
        myLiveData.postValue("My+++++++")
        myLiveData2.postValue("You+++++++")
    }
  }
复制代码

因为作为条件的LiveData 数据为true 切换到了第一个LiveData,所以只能接受到第一个的数据变化,第二个没有接收到

MediatorLiveData.addSource()合并多个LiveData

当需要同时对多个LiveData进行监听时,则该类可实现

   class LiveDataActivity : AppCompatActivity() {
    private val myLiveData = MutableLiveData<String>()
    private val myLiveData2 = MutableLiveData<String>()
    private val myLiveData3 = MutableLiveData<Boolean>()
    private val myLiveData4 = MediatorLiveData<Any>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)
        testMediatorLiveData()
    }

    private fun testMediatorLiveData() {
        myLiveData4.addSource(myLiveData, Observer {
            Log.i("LiveTag1", it)
        })
        myLiveData4.addSource(myLiveData2, Observer {
            Log.i("LiveTag2", it)
        })
        myLiveData4.addSource(myLiveData3, Observer {
            Log.i("LiveTag3", it.toString())
        })
        myLiveData4.observe(this, Observer {
            Log.i("LiveTag4", it.toString())
        })
        myLiveData.postValue("My+++++++")
        myLiveData2.postValue("You+++++++")
        myLiveData3.postValue(true)
        myLiveData4.postValue("He+++++++++++")
    }
 }
复制代码

结果

虽然 MediatorLiveData 可以将多个LiveData合并监听,但是值的变化只在对应的Observer中,这里可定义一个类统一接受或者分别进行处理

LiveDate的缺点???

短时间内多次调用LiveData.postValue()

先看个有趣的现象

class LiveDataActivity : AppCompatActivity() {
    private val myHandler = Handler()
    private val myLiveData = MutableLiveData<String>()
   
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)
        testStick()
    }

    private fun testStick(){
        myLiveData.observe(this){
            Log.i("LiveTag1", it)
        }
        //连续发送值的改变
        myLiveData.postValue("AAAAAAAAAAAAAAAAAA")
        myLiveData.postValue("BBBBBBBBBB")
        myHandler.postDelayed(Runnable {
        	//延迟1ms发送
            myLiveData.postValue("CCCCCCCCCC")
        },1)
    }
}
复制代码

结果 并没有AAA的值发送出去,但是延迟1ms的CCC收到,短时间内LiveData保持了最新设置的值传递,这样是优点 哈哈

LiveData 在短时间内发生多次改变,在值未被分发完之前,观察者只会收到最新的数据变化

接下来看下LiveData值的分发过程

#LiveData类
static final Object NOT_SET = new Object();
private volatile Object mData = NOT_SET;
volatile Object mPendingData = NOT_SET;

protected void postValue(T value) {
        boolean postTask;
        <1>
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        <2>
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
复制代码
  1. 这部分加锁代码保证了mPendingData是最新的值
  2. 保证在mPostValueRunnable未运行完不会重复运行(其实该线程中就是值分发的过程)
#LiveData类
  private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //newValue就是最新设置的值
            setValue((T) newValue);
        }
    };
    
    @MainThread
    protected void setValue(T value) {
        mData = value;
        dispatchingValue(null);
    }
    //使用While分发值
    void dispatchingValue(@Nullable ObserverWrapper initiator) {
     mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
            	//核心方法
                considerNotify(initiator);
                initiator = null;
            }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }
    
    //该方法决定分发给那些Observer
    private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }
复制代码

个人的理解基础上画了个图

代码中知道 实际分发值的时候就是最新的值,原来的值已经被覆盖了

LiveData粘性事件

在使用 LiveData 分发数据的时候,是不会耦合任何 Context 对象的,所以这个机制就从框架层避免了 NPE,OOM 等问题。但于此同时我们也发现了一些问题,比如 LiveData 无法取消黏性事件。如果之前发送的事件,而后注册的观察者也能接收到这条消息,那么是有可能给我们带来麻烦的。

先看个现象

    private fun testStick(){
        myLiveData.observe(this){
            Log.i("LiveTag1", it)
        }
        myLiveData.postValue("AAAAAAAAAAAAAAAAAA")
        myLiveData.observe(this){
            Log.i("LiveTag2", it)
        }
        myHandler.postDelayed(Runnable {
            myLiveData.postValue("CCCCCCCCCC")
        },1)
    }
复制代码

myLiveData 的第二个观察者竟然也收到了 AAA的改动

原因的话 我们在上一个图中,可以看到其内部通过比对版本号,进而实现了后注册的Observer也能收到之前发送过的事件

如果多个界面共用一个ViewModel(其生命周期大于绑定的所有界面),并且复用了同一个LiveData数据源,LiveData数据源在前一个界面被更新过,那么下一个界面在刚刚绑定观察该LiveData时,就会被通知数据发生变化,LiveData最新值会立即推送过来,在一部分场景下可能显得多此一举,或者引发错误。

参考链接<神羅天徵丶>,这里也提出了解决方案,具体代码去看即可.

美团技术团队

此外还有github上有关于LiveDate替换EventBus,RxBus的开源库

LiveDataBus

LiveEventBus

ElegantBus(个人推荐 ☆☆☆☆☆)

慕课网的专栏中也提出了一种方案

object HiDataBus {

    private val eventMap = ConcurrentHashMap<String, StickyLiveData<*>>()
    fun <T> with(eventName: String): StickyLiveData<T> {
        //基于事件名称 订阅、分发消息,
        //由于 一个 livedata 只能发送 一种数据类型
        //所以 不同的event事件,需要使用不同的livedata实例 去分发
        var liveData = eventMap[eventName]
        if (liveData == null) {
            liveData = StickyLiveData<T>(eventName)
            eventMap[eventName] = liveData
        }
        return liveData as StickyLiveData<T>
    }

    //通过一堆的反射,获取livedata当中的mversion字段,来控制黏性数据的分发与否,但是我们认为这种反射不够优雅。
    class StickyLiveData<T>(private val eventName: String) : LiveData<T>() {
        internal var mStickyData: T? = null
        internal var mVersion = 0

        fun setStickyData(stickyData: T) {
            mStickyData = stickyData
            setValue(stickyData)
            //就是在主线程去发送数据
        }

        fun postStickyData(stickyData: T) {
            mStickyData = stickyData
            postValue(stickyData)
            //不受线程的限制
        }

        override fun setValue(value: T) {
            mVersion++
            super.setValue(value)
        }

        override fun postValue(value: T) {
            mVersion++
            super.postValue(value)
        }

        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            observerSticky(owner, false, observer)
        }

        fun observerSticky(owner: LifecycleOwner, sticky: Boolean, observer: Observer<in T>) {
            //允许指定注册的观察则 是否需要关心黏性事件
            //sticky =true, 如果之前存在已经发送的数据,那么这个observer会受到之前的黏性事件消息
            owner.lifecycle.addObserver(LifecycleEventObserver { source, event ->
                //监听 宿主 发生销毁事件,主动把livedata 移除掉。
                if (event == Lifecycle.Event.ON_DESTROY) {
                    eventMap.remove(eventName)
                }
            })
            super.observe(owner, StickyObserver(this, sticky, observer))
        }
    }

    class StickyObserver<T>(
        val stickyLiveData: StickyLiveData<T>,
        val sticky: Boolean,
        val observer: Observer<in T>
    ) : Observer<T> {
        //lastVersion 和livedata的version 对齐的原因,就是为控制黏性事件的分发。
        //sticky 不等于true , 只能接收到注册之后发送的消息,如果要接收黏性事件,则sticky需要传递为true
        private var lastVersion = stickyLiveData.mVersion
        override fun onChanged(t: T) {

            if (lastVersion >= stickyLiveData.mVersion) {
                //就说明stickyLiveData  没有更新的数据需要发送。
                if (sticky && stickyLiveData.mStickyData != null) {
                    observer.onChanged(stickyLiveData.mStickyData)
                }
                return
            }

            lastVersion = stickyLiveData.mVersion
            observer.onChanged(t)
        }

    }
}
复制代码
分类:
Android
标签:
分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改