android 生命周期_Android生命周期组件 Lifecycle 源码详解(一)

5c9b2b6ea713422c174c32d9cbf69530.png

在上篇文章:

warmcheng:Android生命周期组件 Lifecycle 使用详解​zhuanlan.zhihu.com

中,我们讲了 Lifecycle 的简单使用,本篇我们来研究下它的源码。

基础环境搭建

首先,按照上篇文章所讲,快速搭建环境。

添加 Lifecycle 轻量级依赖库:

1    implementation "android.arch.lifecycle:runtime:1.1.1"

添加support library 28.0.0的支持库(希望大家能先保持一致,因为不同版本的源码是有区别的,后面会将到):

1implementation 'com.android.support:appcompat-v7:28.0.0'

再添加个注解处理器相关的依赖,至于用处,后面会讲:

1annotationProcessor "android.arch.lifecycle:compiler:1.1.1"

接下来创建实现了 LifecycleObserver 接口的 MyObserver 类:

5de932e115236861bc0d59028522b2ef.png

让我们的 Activity 继承自 AppCompatActivity,并在 onCreate() 方法中通过 getLifecycle().addObserver(new MyObserver())绑定 MyObserver :

2c8c9fb7691f0c2e8fb1de8af4ee38be.png

核心代码就一句,getLifecycle().addObserver(new MyObserver()) ,就能让我们创建的 MyObserver 类,拥有生命周期感知能力。我们知道,这里主要的对象就两个。一个是 getLifecycle() 方法返回来的 LifecycleRegistry 对象(继承自抽象类 Lifecycle),一个是我们创建的需要监听生命周期的类 MyObserver。那我们不禁要问:LifecycleRegistry 是如何感知到生命周期的?它又是如何把生命周期事件分发给 LifecycleObserver 的?

我们先来解决第一个问题,LifecycleRegistry 是如何感知到生命周期的。

LifecycleRegistry 是如何感知到生命周期的

首先,我们Command/Ctrl + 鼠标左键跟踪 getLifecycle() 代码,发现它的具体实现是在 AppCompatActivity 的祖先类 SupportActivity 中,该类实现了 LifecycleOwner 接口。

74e159b6a1b736fdcefc875e14ea2297.png

在 onSaveInstanceState() 方法中将 mLifecycleRegistry 的状态置为了 Lifecycle.State.CREATED,这点我们在前篇也讲到过。但从这我们还是看不到跟生命周期有关的东西。此时,我们发现在 onCreate() 方法中有这一行代码:

1ReportFragment.injectIfNeededIn(this);

ReportFragment 是做什么的?点进去看:

16d964fb00da74522deeee16183b4f3d.png

可以看到, ReportFragment 的 injectIfNeededIn(Activity activity)方法向 Activity 中添加了一个未设置布局的 Fragment :

8de851f670a5abaf32108b132e999b58.png

然后又在重写的生命周期事件中调用dispatch(Lifecycle.Event event)方法,来分发生命周期事件,这就是“生命周期感知能力”的来源。这种通过一个空的 Activity 或者 Fragment 来实现特定功能的技巧还是挺常见的,比如权限请求库 RxPermission ,以及 airbnb 开源的用于URL跳转的 DeepLinkDispatch(前者是使用空的 Fragment,后者使用的是空的 Activity)

ReportFragment#dispatch(Lifecycle.Event event)

99c2a2078072721ed3eec27dc5f406ca.png

这里面,又调用了 LifecycleRegistry 的handleLifecycleEvent(event)方法。至此,就引入了第二个问题,事件是如何分发到 LifecycleObserver 的。

事件是如何分发到 LifecycleObserver 的

进入 LifecycleRegistry#handleLifecycleEvent(Lifecycle.Event event)方法,发现它又调用了 moveToState(State next) 方法:

208adf3926f6e09496e6c52a346d254f.png

而在 sync() 方法中,根据 state 的状态,最终会调用到backwardPass(...)或者forwardPass(...)

c047f0de5959ddd9863e72aae24b57d3.png

forwardPass(...) 为例:

b57a292d51140939f597b207ebb85039.png

上图可以看到,通过 mObserverMap 最终获取到一个 ObserverWithState 类型的 observer 对象,并调用它的dispatchEvent进行事件分发:

1 observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState));

ObserverWithState 又是个什么鬼?我们继续追踪,发现 ObserverWithState 是 LifecycleRegistry 的一个静态内部类。

2d750d977a64ca58e8607624cfda4ef7.png

从名称上就能看出,该类封装了 Observer 对象和 State 对象(具体就是 StateGenericLifecycleObserver,GenericLifecycleObserver 是个接口,继承自 LifecycleObserver),在其 dispatchEvent 方法中,最终会回调 mLifecycleObserver 的 onStateChanged(...)方法。

追踪到这里,我们知道了,Lifecycle在监听到生命周期变化之后,最终会回调 GenericLifecycleObserver 的 onStateChanged() 方法。我们不由得疑惑,我们定义的 MyObserver 哪去了?没看到有调用我们定义的回调方法啊。它和 GenericLifecycleObserver 又有什么关系?

我们看到,ObserverWithState 的构造函数里传进来了一个 LifecycleObserver 类型的 observer 对象,这个参数是从哪传进来的?继续追踪,发现追到了LifecycleRegistry#addObserver(LifecycleObserver observer)方法。
而这个方法,就是我们在MainActivity#onCreate(...)方法中调用的:

1 getLifecycle().addObserver(new MyObserver());

到这里,总算跟我们的 MyObserver 关联上了。查看LifecycleRegistry#addObserver(LifecycleObserver observer)方法源码:

307a514e1f2fcd6cecdf77fa404eb966.png

这里面的核心代码就两行,一行是:

1 ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);

这行代码,通过传进来的Observer对象,创建出 ObserverWithState 对象。还有一行是:

1 ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);

这行代码是将 LifecycleObserver 对象放入一个FastSafeIterableMap 中,以便进行迭代。

接下来我们就进入 ObserverWithState 的构造方法中看看:

2d750d977a64ca58e8607624cfda4ef7.png

在构造方法中,通过 Lifecycling.getCallback(observer)根据传进来的 observer ,构造了一个 GenericLifecycleObserver 类型的 mLifecycleObserver ,那秘密应该也就在这个方法里,继续跟进。

aa836e6636a835b79ecde8301fa32894.png

这个方法的本质,其实就是根据传进来的一个LifecycleObserver 对象,构造出来一个 GenericLifecycleObserver 对象(目前有四个子类:FullLifecycleObserverAdapterSingleGeneratedAdapterObserverCompositeGeneratedAdaptersObserverReflectiveGenericLifecycleObserver),而最终构造出来的对象,就包含了我们创建的 LifecycleObserver 的所有信息,包括各种回调方法等。

看到这里,就要提到文章开头要大家添加的一个注解处理器的依赖:

1annotationProcessor "android.arch.lifecycle:compiler:1.1.1"

当我们通过注解的方式来自定义LifecycleObserver 的时候,按照传统方式,必定要通过反射来对注解进行解析,这样就会对性能造成影响。一方面,我们通过缓存,来避免每次都通过反射获取构造器。另一方面,又通过注解处理器,在编译时对那些被@OnLifecycleEvent注解标注的普通方法,进行预处理,生成以“类名_LifecycleAdapter”命名的类,将各种回调方法直接进行逻辑转换,避免反射,进而来提高性能。

明白了这点,再看Lifecycling.getCallback(observer)方法就比较容易理解了。

  1. 如果传进来的的参数 object 是 FullLifecycleObserver 类型,就把它构造成FullLifecycleObserverAdapter 对象,并返回
  2. 如果传进来的的参数 object 是GenericLifecycleObserver类型,直接返回该对象
  3. 如果1,2都不满足,就解析该类的的构造器的Type(该类是反射获取的,还是通过注解处理器生成的)。如果是通过注解处理器生成的类来调用回调函数,就返回一个SingleGeneratedAdapterObserver/CompositeGeneratedAdaptersObserver 对象
  4. 如果以上条件都不满足,就通过反射来调用各回调函数。返回一个 ReflectiveGenericLifecycleObserver 对象

现在我们在 app 目录下的 bulid.gradle 中添加上上面的注解处理器依赖,然后编译下项目,会发现在build目录下生成了对应的类:MyObserver_LifecycleAdapter.java

358abb79da6ead145e45c5069917f322.png

点进去,看看生成的这个类的源码:

a3a830ed0b1c1646e0367b8383047f6b.png

可以看到,我们在 MyObserver 中通过@OnLifecycleEvent注解标注的那些方法,在这里都根据条件进行判断了,而非通过注解。

这时候我们就能理清这个这个流程了,当添加了注解处理器之后,我们这里的Lifecycling.getCallback(observer)方法将会把我们的MyObserver对象构建成一个 SingleGeneratedAdapterObserver对象返回(因为这里只有一个构造器),之后的 mLifecycleObserver.onStateChanged(owner, event);其实调用的就是SingleGeneratedAdapterObserveronStateChanged(owner, event)方法:

920a30bb7f23427cb6533745c7ef26b1.png

这里面就可以看到,它调用了内部包裹的类的callMethods(...)方法,也就是我们上面提到的MyObserver_LifecycleAdaptercallMethonds(...)方法。

到这里,就完成了 Lifecycle 源码的解析。

通过反射获取注解信息

这顺便提下通过注解的方式调用各回调方法的过程。主要相关类就是 ReflectiveGenericLifecycleObserver.java

e419ec99421b53901e697060a220649c.png

这里我们主要关注回调信息 CallbackInfo 的获取方式的代码: mInfo = ClassesInfoCache.sInstance.getInfo(mWrapped.getClass());

因为反射的代价是比较大的,所以又通过 ClassesInfoCache.java这个单例类,为 ReflectiveGenericLifecycleObserver 类要调用的各种方法的相关信息进行了缓存。

点进去看下它的 getInfo(...) 方法内部,是如何获取方法信息的。

156287ff99840256bcd84e072ecba323.png

里面又调用了createInfo()方法:

5db1d3e2b2fd5a22b051b2ac6db09068.png

这里,就能看到对注解进行处理的代码了。

到这,我们就算完成了继承自 AppCompactActivity 的情况下的源码解析,而继承自普通 Activity 这种情况下,原理是什么呢?

鉴于篇幅,将放在下篇文章。欢迎关注我的公众号获取。

542ba168dcfc551c4daf98eea2f3d4fd.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246041.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode1143. 最长公共子序列(c#)

题解&#xff1a;力扣 public class Solution{public int LongestCommonSubsequence(string text1, string text2){int num1 text1.Length;int num2 text2.Length;int[,] dp new int[num1 1, num2 1];for(int i 0; i < num1; i){for(int j 0; j < num2; j){if(t…

telnet到设备里 php_PHP自动生成设备周检修计划

背景维修人员根据设备年度检修计划&#xff0c;然后制订周检修计划(设备年度计划包含设备一年需要维护几次等信息&#xff0c;根据年度计划分解到某一个周去执行)。在这个过程中&#xff0c;大量的excel复制粘贴工作&#xff0c;浪费人力并且容易出错。并且在审核过程中&#x…

通俗讲解:图像傅里叶变换

转自某乎&#xff1a;通俗讲解&#xff1a;图像傅里叶变换 - 知乎 这里我们主要要讲的是二维图像傅里叶变换&#xff0c;但是我们首先来看一张很厉害的一维傅里叶变换动图。 妈耶~厉害哇&#xff01;它把时域和频域解释的很清楚&#xff01; 什么&#xff01;你看不懂&#x…

数据库断线重连_干货分享—Niushop数据库配置

前几期阿牛ger主讲了代码编码规范&#xff0c;整洁规范的代码有利于我们查询和再次开发&#xff0c;也方便我们检测与修复bug&#xff01;这期&#xff0c;阿牛ger主要与大家分享数据库编码配置&#xff1a;数据库配置Niushop数据库配置方式与thinkphp相同&#xff0c;文件路径…

怎么将matlab滤波器系数导出_matlab与FPGA数字信号处理系列(1)——通过matlab工具箱设计FIR数字滤波器...

以99阶FIR低通滤波器为例&#xff0c;学习使用matlab的fdatool工具箱设计滤波器&#xff0c;并将滤波器系数导出到.coe文件&#xff0c;联合Vivado进行FPGA的FIR滤波器设计。本文滤波器参数为&#xff1a;低通FIR滤波器&#xff0c;窗函数设计&#xff0c;采用布莱克曼窗&#…

UGUI 合批原理

转自&#xff1a; UGUI合批原理笔记 - 赵青青 - 博客园 UGUI合批规则图解_时光不染-CSDN博客_ugui合批规则 合批的过程# 网格更新机制# Cavans.SendWillRenderCanvas m_LayoutRebuildQueuem_GraphicRebuildQueueCanvas.BuildBatch 更新所有DrawCall WaitingForJob 子线程网格…

vb.net 设置打印纸张与页边距_装订文档时不想让文字被挡住?在Excel中你可以这样设置打印!...

平时我们在打印文档的时候&#xff0c;通常会把文档左侧的页边距设置的大一点&#xff0c;这样在装订的时候显得美观一点。但如果我们进行双面打印时&#xff0c;文档左右两边的页边距刚好相反&#xff0c;装订时第2页的文本很容易被挡住&#xff0c;这样子反而更难装订了。那么…

c语言 pow优化_c程序代码优化的一些方法

我认为一个好的用于科学计算的程序代码应该&#xff1a;算法漂亮精妙&#xff0c;程序简洁易懂&#xff0c;运算快速&#xff0c;节省内存。这里有的地方是矛盾的&#xff0c;比如简洁vs易懂&#xff0c;时间vs空间&#xff0c;找个平衡吧。目前来看时间要比空间宝贵一些。写程…

CPU Cache原理与示例

转自这篇 CPU Cache&#xff0c;估计也没人看 基础知识 首先&#xff0c;我们都知道现在的 CPU 多核技术&#xff0c;都会有几级缓存&#xff0c;老的 CPU 会有两级内存&#xff08;L1 和 L2&#xff09;&#xff0c;新的CPU会有三级内存&#xff08;L1&#xff0c;L2&#x…

python集合的基本操作不包括_Python基础知识储备,List集合基本操作大盘点

List列表是Python中最基本的数据结构&#xff0c;也是Python中使用频率最高的数据类型&#xff0c;List列表中的元素不需要具有相同类型&#xff0c;使用起来非常方便。现在就来体验一下List列表的基本操作。 list集合基本操作 List的基本操作&#xff08;&#xff0c;copy&…

mysql 恢复root用户_mysql误删root用户恢复方案

linux下误删mysql的root用户&#xff0c;解决方法开始对liunx界面不熟悉&#xff0c;可能由于不小心&#xff0c;把root误删了&#xff0c;怎么办&#xff1f;1. # killall mysqld 干掉所有mysql进程2. # mysqld_safe --skip-grant-tables & 进入mysql安全模式3. 通过…

解决递归栈溢出:尾递归

原文&#xff1a;尾递归_百度百科 尾递归 如果一个函数中所有递归形式的调用都出现在函数的末尾&#xff0c;我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时&#xff0c;这个递归调用就是尾递归。尾递归函数的特点…

mysql blob hex_数据库的完整备份与恢复 quot;--hex-blobquot; - - ITeye博客

闲言少絮&#xff0c;这个程序利用MySql数据库自带小程序进行数据库的备份和还原。这两个程序分别是&#xff1a;mysql.exe和mysqldump.exe。这两个程序在您安装Mysql数据库的时候会自动安装到数据库的bin目录。这两个程序存在的目录为&#xff1a;C:\Program File\MySQL\MySQL…

python实现多表格合并_用python实现多个表格合并按字段去重

需求 在xx银行项目中遇到的一个需求&#xff0c;是将系统中的8张余额表下载到指定的文件夹中&#xff0c;然后从文件夹中读取这8张余额表&#xff0c;将其合并为一张余额汇总表&#xff0c;在合并的时候要将组合名称重复的多行数据去重&#xff0c;并将其所对应的余额相加 实现…

C# Lambda 和 匿名函数的GC总结

关于Lambda和 匿名函数&#xff0c;闭包的GC&#xff0c;其实可以总结为两条。 为了方便理解&#xff0c;以举例说明&#xff0c;首先我们定义变量&#xff0c;静态变量&#xff0c;以及函数如下&#xff1a; static int staticVariable 0;int variable 0;private void Func…

Unity使用sdkmanager命令行工具安装Android SDK

转自&#xff1a;使用sdkmanager命令行工具安装Android SDK Unity自带的SDKManager没有GUI工具&#xff0c;如果需要其他android SDK版本&#xff0c;可以用Unity自带的SDKManager使用命令行方式安装&#xff1a; 例如安装android27,将目录切到Unity的Android SDK安装目录后&…

mysql c api 封装_封装MySQL C API 基本操作

根据我的以前的文章 http://blog.csdn.net/skyhuangdan/article/details/21099929 链接数据库成功后进行封装。我封装类使用的是VS2005下的win32控制台应用程序编写&#xff0c;预编译头文件了的。所以要在 stdafx.h 里面加入 &#xff1a;#include "CMySQL.h"现在代…

android中怎么保存checkbox中的checked属性_Vue 精粹:v-model指令在组件中怎么玩

最近在写组件的时候&#xff0c;遇到了 v-model 的使用问题&#xff0c;在 Vue 官方文档中&#xff0c;有两小端内容是关于 v-model 指令在组件中的使用,查阅文档后&#xff0c;依然不得要领&#xff0c;最后几番折腾&#xff0c;理论结合实践&#xff0c;终于领悟其精髓&#…

xcode 自动化出包

iOS提高效率之Xcodebuild自动打包总结 - 简书 使用xcodebuild命令进行自动化打包 - 简书 Add a profile to the provisioningProfiles dictionary in your Export Options property list. | 亂馬客 - Re:從零開始的軟體開發生活

linux location root访问文件夹404_如何使网站支持https访问?nginx配置https证书

购买SSL证书要想使用https访问你的网址&#xff0c;首先得拥有颁发的SSL证书。我使用的是免费版&#xff0c;有效期为一年&#xff0c;过期后再重新申请。申请SSL证书购买后&#xff0c;可在搜索框输入证书关键字进入到控制台。点击证书申请&#xff0c;按照提示填写完相关信息…