LiveData基本使用及源码解析(一)

243 阅读9分钟

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源码

        image-20211202232832355

      • 进入接口实现

        image-20211202233020253

      • 获取到此时被观察者状态:INITIALIZED

        image-20211202233136300

      • 对应状态机:可知此时应该执行ON_CREATE事件,此时被观察者的状态被更新为CREATED;

      • 状态对齐:

        • 此时被观察者的状态被更新为CREATED,观察者的状态为RESUMED;

        • 通过比较枚举类型大小,发现此时的被观察者状态应该向前更新

          • 其实,可以理解为比较事件的状态的枚举值

            • 此时被观察者:执行ON_CREATE事件后,状态更新为CREATED,对应的枚举值假设为1
            • 此时的观察者:由于是在onResume函数中进行添加观察者的,那么此时可以理解为观察者执行了ON_RESUME事件后状态更新成了RESUMED,对应的枚举值为3
          • 在下图红色框中进行状态对齐操作:

            • 这个循环只考虑前进不考虑后退
            • 设计初衷:在onStart()或者onCreate()方法中进行注册的
          • 源码展示:

            image-20211202234401820

      • 在onResume中进行Lifecycle.addObserver事件/状态的更新流程

        • 事件更新:ON_CREATE(最开始,还没有进入状态对齐的循环,初始化操作)--->ON_START(第一次循环)--->ON_RESUME

        • 状态更新:

          1. INITIALIZED:初始化操作(来就是这样的)

            image-20211202235719308

          2. CREATED

            image-20211202235734302

          3. STARTED

            image-20211202235747226

          4. RESUMED

            image-20211202235801953

LiveData原理解析

  • 概述

    • 功能实现依赖于LifeCycle原理

    • LiveData既是观察者又是被观察者:实现数据驱动UI

      • 图示:

        • 红色:

          • 读取LifeCycle状态:当为STARTED或者RESUMED,即认为有更新UI的能力,对于其他状态来说,LiveData不工作
        • 一端观察数据变化(蓝色),一端根据数据变化进行更新UI(红色)

          图片.png

      • 实现细节:

        • 这个眼睛是可以添加多个的
  • 基本使用

    • 工程结构

      image-20211203005936799

    • 实现效果:

      • 开启两个线程计时,达到某一计时点时触发UI更新

      • 图示:三张图片

        • 首页:

          图片.png

        • 三秒时:

          图片.png

        • 六秒时:

          图片.png

    • 实现思路:

      • 创建单例类,配合懒加载技术(当需要使用这个类时,才加载这个类)

         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

              image-20211203134206158

            • 查看父类

              image-20211203134443450

            • 继续追踪主线流程

              image-20211203134542672

            • 最终就会调用到setValue(主线程)

              image-20211203134658416

        • 完整代码:

           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:启动服务+添加img ,获取宿主环境(this),在匿名函数中承接被观察者MyService中的数据变化;将其作为更新UI的数据源
    • 运行截图:

      • 当界面可见时:

        • Logcat

          image-20211203141250851

        • Android image-20211203142135744

      • 当界面不可见:按下home键,后台运行程序;退出,程序就停止了

        • Logcat

          image-20211203141330433

        • Android

          image-20211203142226769

      • 再次打开应用

        • Logcat

          image-20211203141403107

        • Android

          图片.png

    • 代码示例:

      • 工程结构:

        图片.png

      • 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()
                   }
               }
              
          • 运行截图展示

            • 首页:

              image-20211203152942389

            • 点击中间按钮:

              图片.png

          • 实现思路:

            • 创建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,但是数据粘性的存在,会将历史数据也会纳入观察者的眼睛

        • 图示:

          图片.png

          • 订阅过程

            • 在MainActivity4中订阅:

              image-20211203155811190

          • 处理参数一

            • 为什么参数一为this

              • this代表宿主<--->被观察者的声明周期<--->就是Activity的生命周期

              • 查看接口,LifecycleOwner owner:对此代码进行逐行分析

                image-20211203155919639

            对LifecycleOwner owner有,AndroidX环境下,每个新建的Activity是默认实现了这个接口的,这个接口代表宿主(被观察者的生命周期):MainActivity

          • 查看继承关系:对上述予以证明,

            • 进入MainActivity父类AppCompatActivity

              image-20211203160229742

            • 进入AppCompatActivity的父类FragmentActivity

              image-20211203183335959

            • 进入FragmentActivity的父类ComponentActivity

              image-20211203184447043

            • 在ComponentActivity中看到是实现了这个接口:LifecycleOwner owner

              image-20211203184623341

          • 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了一个接口:

              image-20211203190422295

            • 进入Observer类

              image-20211203190453120

          • 处理函数体:

            • 函数体图示:

              image-20211203215155016

            • 171行:判断是否为主线程

              image-20211203191659131

              • 不是主线程,抛出异常

                image-20211203191827312

            • 处理if循环

              image-20211203193003292

            • 第176行

              • 图示:

                image-20211203220236860

              • 概述:这个是对观察者(函数的第二个参数)做二次封装,使其获得感知LifeCycle的能力:这个就是一双眼睛了;并且,构造方法中,传入了宿主环境(被观察者),以及眼睛(观察者)

              • 细节判断:进入LifecycleBoundObserver类

                • 图示:

                  image-20211203223802315

              • 进入:activeStateChanged类

                image-20211203223642573

              • 进入:dispatchingValue(this);

                • 此时的this:ObserverWrapper(LifecycleBoundObserver父类)

                image-20211203225949807

              • 进入:considerNotify(initiator);

                • 黏性来源

                  • 粘性数据会覆盖,就像一个栈,粘性数据都不断入栈,出栈Pop栈顶

                  • 这个紫色框里面:

                    • 第一行:版本对齐

                      • 防止错乱,观察者太多了
                      • 只会有一处会++
                    • 第二行:调用onChanged函数,更新UI

                image-20211203231526016

                轮询操作

                图片.png

                • 点进紫色框中最后一行代码的onChanged:更新UI

                  • 也就是为什么在注册的时候,会重写一下onChanged函数

                  image-20211204000937170

              在调用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

      image-20211203233957485

      • 这里面封装了一个类:

        • 装饰者模式:将父类LiveData中符合用户需求的给他

          image-20211203234210372

        • 进入:

          image-20211203234807482

          • 进入上图红色:mPostValueRunnable

            image-20211203234555969

            • 进入setValue (T)

              image-20211204000256668

              • 为空时:进入循环(遍历多次观察者,可能有多个观察者)

                image-20211204000406703

          • 进入上图蓝色

            image-20211203235217498

        • 进入上图绿色

          图片.png

        • 选择任务栈:点这个

          image-20211203235406381

        • 通过Handler:子线程中的postValue最终会调用主线程中的setValue

          image-20211203235628515