Livecycle源码解析提炼
-
如何感知事件:核心在于保存宿主(被观察者)字节码文件
-
经验:通过源码跳转,观察者的字节码文件存放在Map中,处理成缓存调用(第一碰到就放到缓存里面,第二次再碰到就直接去调用就行了)
-
保存位置:
-
ReflectiveGenericLifecycleObserver
ReflectiveGenericLifecycleObserver(Object wrapped) { mWrapped = wrapped; mInfo = ClassesInfoCache.sInstance.getInfo(mWrapped.getClass()); } -
ClassesInfoCache.createInfo
CallbackInfo info = new CallbackInfo(handlerToEvent); mCallbackMap.put(klass, info); mHasLifecycleMethods.put(klass, hasLifecycleMethods); return info;
-
-
-
如何去反射执行观察者中的代码(事件分发)
-
有一个空白的Fragment黏贴在被观察者上,被观察者处在某一生命周期中,Fragment也调用相应生命周期的函数(六个生命周期都有对应的)
-
事件分发
- 调用dispatch(进行事件分发),被观察者的不同生命周期对应着相应的事件,
- 根据这个事件,结合状态机,拿到执行这个事件后对应的被观察者的状态
- 拿到状态(被观察者),与此时观察者的状态做比较,判断此时观察者的状态决定是先前还是向后更新(结合状态机),并返回实现更新操作需要执行事件;
- 将此事件作为dispatch的参数,调用invoke,然后执行观察者中对应的声明周期(通过注解进行注入的)中的函数
-
-
注册流程:
-
在onStop函数中将观察者与被观察者关联:
- 状态更新:initial--->create--->要执行onCreate
- 事件:onCreate + onStop
- 不是initial就是销毁状态,一开始为intitial,对应的事件是onCreate--->CREATED
- addObserver,永远是向前推进的,upEvent()函数
-
在onResume中进行Lifecycle.addObserver
-
查看addObserver源码
-
进入接口实现
-
获取到此时被观察者状态:INITIALIZED
-
对应状态机:可知此时应该执行ON_CREATE事件,此时被观察者的状态被更新为CREATED;
-
状态对齐:
-
此时被观察者的状态被更新为CREATED,观察者的状态为RESUMED;
-
通过比较枚举类型大小,发现此时的被观察者状态应该向前更新
-
其实,可以理解为比较事件的状态的枚举值
- 此时被观察者:执行ON_CREATE事件后,状态更新为CREATED,对应的枚举值假设为1
- 此时的观察者:由于是在onResume函数中进行添加观察者的,那么此时可以理解为观察者执行了ON_RESUME事件后状态更新成了RESUMED,对应的枚举值为3
-
在下图红色框中进行状态对齐操作:
- 这个循环只考虑前进不考虑后退
- 设计初衷:在onStart()或者onCreate()方法中进行注册的
-
源码展示:
-
-
-
在onResume中进行Lifecycle.addObserver事件/状态的更新流程
-
事件更新:ON_CREATE(最开始,还没有进入状态对齐的循环,初始化操作)--->ON_START(第一次循环)--->ON_RESUME
-
状态更新:
-
INITIALIZED:初始化操作(来就是这样的)
-
CREATED
-
STARTED
-
RESUMED
-
-
-
-
LiveData原理解析
-
概述
-
功能实现依赖于LifeCycle原理
-
LiveData既是观察者又是被观察者:实现数据驱动UI
-
图示:
-
红色:
- 读取LifeCycle状态:当为STARTED或者RESUMED,即认为有更新UI的能力,对于其他状态来说,LiveData不工作
-
一端观察数据变化(蓝色),一端根据数据变化进行更新UI(红色)
-
-
实现细节:
- 这个眼睛是可以添加多个的
-
-
-
基本使用
-
工程结构
-
实现效果:
-
开启两个线程计时,达到某一计时点时触发UI更新
-
图示:三张图片
-
首页:
-
三秒时:
-
六秒时:
-
-
-
实现思路:
-
创建单例类,配合懒加载技术(当需要使用这个类时,才加载这个类)
package com.derry.livedata.livedata.simple1 import androidx.lifecycle.MutableLiveData object MyLiveData { // 单例类 // 使用懒加载,在使用的时候才去加载这个类,提升效率 val info1: MutableLiveData<String> by lazy { MutableLiveData() } } -
创建MainActivity:触发数据变化+监听变化并更新UI
-
监听变化并更新UI
-
方式一:Lamda表达式+this(宿主<-->被观察者)环境
MyLiveData.info1.observe(this, { textView.text = it // 更新UI }) -
方式二:完整写法 new Observer + 重写onChanged()函数
MyLiveData.info1.observe(this, object: Observer<String> { override fun onChanged(t: String?) { textView.text = t // 更新UI } })
-
-
触发数据变化
-
主线程:数据初始化
// setValue 主线程修改数据变化 MyLiveData.info1.value = "default" -
子线程:作数据改变操作
// postValue 子线程 thread { Thread.sleep(3000) MyLiveData.info1.postValue("三秒钟后,修改了哦") } // postValue 子线程 thread { // 子线程 Thread.sleep(6000) MyLiveData.info1.postValue("六秒钟后,修改了哦") } -
-
知识细节:Kotlin语法细节(两种方式是等价的)
//主线程中 变量.value = "value" <---> 变量.setValue("value") println(变量.value) <---> println(变量.getValue) //子线程中 变量.postValue = "value" <---> 变量.setValue("value") -
postValue使用:非主线程修改数据,界面正常更新
-
因为postValue(子线程)最终会调用到setValue(主线程)的
-
进入postValue
-
查看父类
-
继续追踪主线流程
-
最终就会调用到setValue(主线程)
-
-
-
完整代码:
package com.derry.livedata.livedata.simple1 import android.os.Bundle import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import com.derry.livedata.R import kotlin.concurrent.thread class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val textView : TextView = findViewById(R.id.tv_textview) //关注数据变化并更新UI,Lamda表达式+this(宿主<-->被观察者)环境 MyLiveData.info1.observe(this, { textView.text = it // 更新UI }) /* // 完整写法 new Observer + 重写onChanged()函数 MyLiveData.info1.observe(this, object: Observer<String> { override fun onChanged(t: String?) { textView.text = t // 更新UI } }) */ //触发数据改变,setValue 主线程修改数据变化 MyLiveData.info1.value = "default" // thread { Thread.sleep(3000) MyLiveData.info1.postValue("三秒钟后,修改了哦") // postValue 子线程 } thread { // 子线程 Thread.sleep(6000) MyLiveData.info1.postValue("六秒钟后,修改了哦") // postValue 子线程 } } }
-
-
-
总结:LiveData替代了eventBus,DataBinding替代了LiveData
-
-
LiveData使用(综合服务,模仿后台推送数据)
-
实现效果:
-
当界面可见的时:Toast提示信息(更新UI)+后台打印日志
-
当界面不可见的时:后台打印日志
-
当界面恢复可见时(退出程序--->后台运行--->再次进入程序):
- Toast提示信息(更新UI)+后台打印日志(忽略之前的数据)
-
-
实现思路:
- 创建单例类MyLiveData配合懒加载+init操作
- 创建服务类MyService:在其中开启子线程并使用postValue对数据作修改
- 创建MainActivity2:启动服务+添加
,获取宿主环境(this),在匿名函数中承接被观察者MyService中的数据变化;将其作为更新UI的数据源
-
运行截图:
-
当界面可见时:
-
Logcat
-
Android
-
-
当界面不可见:按下home键,后台运行程序;退出,程序就停止了
-
Logcat
-
Android
-
-
再次打开应用
-
Logcat
-
Android
-
-
-
代码示例:
-
工程结构:
-
MyLiveData
-
细节:在使用模拟器时,违背在子线程中setValue就会抛出异常;
这里会有一个线程冲突
package com.derry.livedata.livedata.simple2 import androidx.lifecycle.MutableLiveData object MyLiveData { // 单例 // 这里为info1的MutableLiveData 懒加载初始化(懒加载:用到时才加载) val data1 : MutableLiveData<String> by lazy { MutableLiveData() } init { // data1.value = "default" // 违背在 子线程 setValue data1.postValue("原始状态") // 子线程 执行 postValue 非常OK } } -
-
MyService
- MyLiveData.data1.postValue("服务器给推你推送消息啦,消息内容是:${x}")//这个就是子线程中触发数据改变后更新的数据,作为主线程更新的数据源扔到主线程中去的
package com.derry.livedata.livedata.simple2 import android.app.Service import android.content.Intent import android.os.IBinder import android.util.Log import kotlin.concurrent.thread // 模拟后台推送 class MyService : Service() { override fun onBind(intent: Intent): IBinder ? = null //服务是主线程,所以开一个线程,在子线程中触发数据改变 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { thread { for (x in 1..100000) {//kotlin的区间 Log.d("server", "服务器给推你推送消息啦(叮咚声响),消息内容是:${x}") MyLiveData.data1.postValue("服务器给推你推送消息啦,消息内容是:${x}")//这个就是子线程中触发数据改变后更新的数据,作为主线程更新的数据源扔到主线程中去的 Thread.sleep(5000) // 5秒钟推一次 } } return super.onStartCommand(intent, flags, startId) } } -
MainActivity2
-
//这个it就是子线程中更新的数据
MyLiveData.data1.observe(this, { Log.d("server", "应用界面对用户可见,说明用户正在使用应用,更新UI界面:${it}")
}
package com.derry.livedata.livedata.simple2 import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import android.widget.Button import android.widget.Toast import com.derry.livedata.R class MainActivity2 : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) // 启动服务 val button = findViewById<Button>(R.id.button) button.setOnClickListener { startService(Intent(this, MyService::class.java)) Toast.makeText(MainActivity2@this, "启动服务器推送成功", Toast.LENGTH_SHORT).show() } // 眼睛 观察者 界面可见的情况下,才能做事情 //在主线程中进行UI更新 MyLiveData.data1.observe(this, { Log.d("server", "应用界面对用户可见,说明用户正在使用应用,更新UI界面:${it}") //这个it就是子线程中更新的数据 Toast.makeText(this, "更新消息列表UI界面成功:${it}", Toast.LENGTH_SHORT).show() }) } } -
-
-
线程冲突:在模拟器中
-
在服务类中,开启子线程包裹单例类,并在此处进行单例类初始化;
-
在MyLiveData类中,编辑init函数,里面使用setValue;
这样就造成了线程冲突:抛出异常
-
-
对比Handler:
- 在sendMessage的时候,不管界面是否可见(不会去检测生命周期),直接就去更新UI;这可能会造成内存泄漏,所以一般都使用LiveData
-
-
LiveData数据粘性展示:
-
概述:
-
数据粘性:先修改数据,再订阅,此时可以接收到订阅之前的数据
-
正常情况:先订阅,再修改数据,最后收到数据
-
创建一个接口:里面写个函数
package com.derry.livedata.livedata.simple3 interface Callback { fun show(); }-
在Activity中:先订阅,后触发
class MainActivity4 : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main4) // 先订阅 ok(object : Callback { override fun show() { } }) } fun ok(callback: Callback) { // 后触发 callback.show() } }
-
-
运行截图展示
-
首页:
-
点击中间按钮:
-
-
实现思路:
-
创建MyLiveData:单例+懒加载
package com.derry.livedata.livedata.simple3 import androidx.lifecycle.MutableLiveData object MyLiveData { // 这里为info1的MutableLiveData 懒加载初始化(懒加载:用到时才加载) val value1 : MutableLiveData<String> by lazy { MutableLiveData() } } -
创建首页Activity:MainActivity3
-
按钮监听:修改数据并进行Activity跳转
package com.derry.livedata.livedata.simple3 import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button import com.derry.livedata.R class MainActivity3 : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main3) val button = findViewById<Button>(R.id.button) button.setOnClickListener { //先修改数据再跳转Activity MyLiveData.value1.value = "在跳转之前,对数据进行修改" // 以前的旧数据 startActivity(Intent(this, MainActivity4::class.java)) } } }
-
-
创建跳转后的Activity:MainActivity4
-
在onCreate方法中实现订阅,尝试接收上一个Activity对数据的修改
package com.derry.livedata.livedata.simple3 import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Toast import androidx.lifecycle.Observer import com.derry.livedata.R // this@MainActivity4 每个类都有符号 class MainActivity4 : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main4) // 我后观察数据,居然能够收到前面修改的数据,这就是数据黏性 MyLiveData.value1.observe(this, { Toast.makeText(this, "此时已经跳转到下一个Activity了,尝试打印上一个Activity中的数据:$it", Toast.LENGTH_SHORT).show() }) } }
-
-
-
-
-
-
-
源码解析:LiveData.observe
-
概述:
-
去除粘性:hook+反射:动态修改源码就可以去除
-
先setValue,后订阅,就一定有黏性;
-
粘性的Bug:实现由后续新数据驱动UI,但是数据粘性的存在,会将历史数据也会纳入观察者的眼睛
-
图示:
-
订阅过程
-
在MainActivity4中订阅:
-
-
处理参数一
-
为什么参数一为this
-
this代表宿主<--->被观察者的声明周期<--->就是Activity的生命周期
-
查看接口,LifecycleOwner owner:对此代码进行逐行分析
-
对LifecycleOwner owner有,AndroidX环境下,每个新建的Activity是默认实现了这个接口的,这个接口代表宿主(被观察者的生命周期):MainActivity
-
-
查看继承关系:对上述予以证明,
-
进入MainActivity父类AppCompatActivity
-
进入AppCompatActivity的父类FragmentActivity
-
进入FragmentActivity的父类ComponentActivity
-
在ComponentActivity中看到是实现了这个接口:LifecycleOwner owner
-
-
this疑问二:对比observe与observeForever
-
observe:持有宿主,
-
observeForever:不持有宿主,一股脑更新跟handler一样
- 看需求,也可以用(使用LiveData实现Handler功能)
MyLiveData.value1.observe(this, object: Observer<String>{ override fun onChanged(t: String?) { Toast.makeText(this@MainActivity4, "观察者数据变化$t", Toast.LENGTH_SHORT).show() } }) /*// 此观察者 和 handler没有区别,一股脑的执行 (极端的情况,可以用) // 手动考虑释放工作 MyLiveData.value1.observeForever({ })
-
-
处理第二个参数:object:Observer
-
就是new了一个接口:
-
进入Observer类
-
-
处理函数体:
-
函数体图示:
-
171行:判断是否为主线程
-
不是主线程,抛出异常
-
-
处理if循环
-
第176行
-
图示:
-
概述:这个是对观察者(函数的第二个参数)做二次封装,使其获得感知LifeCycle的能力:这个就是一双眼睛了;并且,构造方法中,传入了宿主环境(被观察者),以及眼睛(观察者)
-
细节判断:进入LifecycleBoundObserver类
-
图示:
-
-
进入:activeStateChanged类
-
进入:dispatchingValue(this);
- 此时的this:ObserverWrapper(LifecycleBoundObserver父类)
-
进入:considerNotify(initiator);
-
黏性来源
-
粘性数据会覆盖,就像一个栈,粘性数据都不断入栈,出栈Pop栈顶
-
这个紫色框里面:
-
第一行:版本对齐
- 防止错乱,观察者太多了
- 只会有一处会++
-
第二行:调用onChanged函数,更新UI
-
-
轮询操作
-
点进紫色框中最后一行代码的onChanged:更新UI
- 也就是为什么在注册的时候,会重写一下onChanged函数
-
在调用MyLiveData.value1.observeForever这个函数的时候,需要做特殊处理:当被观察者的状态为销毁状态时,手动解绑观察者
-
实现思路:首先,这种做法放弃了LiveData的优点
class MainActivity4 : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main4) // 此观察者 和 handler没有区别,一股脑的执行 (极端的情况,可以用) // 手动考虑释放工作 MyLiveData.value1.observeForever({ 观察者 }) } override fun onDestroy() { super.onDestroy() // 手动释放 MyLiveData.value1.removeObserver(将上面的那个观察者移动这个里面,不能放在上面) } }
-
-
-
-
-
-
-
触发流程:
-
postValue
-
这里面封装了一个类:
-
装饰者模式:将父类LiveData中符合用户需求的给他
-
进入:
-
进入上图红色:mPostValueRunnable
-
进入setValue (T)
-
为空时:进入循环(遍历多次观察者,可能有多个观察者)
-
-
-
进入上图蓝色
-
-
进入上图绿色
-
选择任务栈:点这个
-
通过Handler:子线程中的postValue最终会调用主线程中的setValue
-
-
-