你真的了解Emoji吗?Emoji全貌大揭秘

前言

随着科技发展,智能手机的普及,Emoji已经融入到了我们的生活中,但每天使用Emoji的你真的清楚它是什么,是由什么东西组成的,和普通的字符有什么区别吗?本文就从技术的角度带你揭秘Emoji的全貌。

起因

小红书里有一个AI聊天助手”达芬奇”,你可以向他提问,他会以流式打印的形式一字一字的将回答呈现给用户。在这过程中我就发现,每当打印到Emoji的时候,总会先出现一个问号形状的乱码,然后才能显示出Emoji,有的Emoji更奇特,以👨‍👩‍👧‍👦为例,在打印过程中会依此显示👨👩👧👦四个Emoji,最后突然啪的一下,合成一整个👨‍👩‍👧‍👦,是不是很神奇?这个现象引起了我的好奇,于是我开始翻阅资料,揭开Emoji的神秘面纱。

Emoji的起源及发展

Emoji来自日语词汇”絵文字”(假名为“えもじ”,读音即 emoji),绘指图画,文字指字符,最早由栗田穰崇(Shigetaka Kurita)创作,设计灵感源于天气预报图标、汉字、漫画和路标等,最初的Emoji有176个,都是12 x 12像素的图片。

1999年,日本通讯运营商DOCOMO公司发布了在当时具有跨时代意义的iMode手机,最早的Emoji便搭载于其中。

Emoji一经诞生,人们便发现这些形象的Emoji实在是太好用了,不仅方便,还能使聊天过程更加有趣,随即便立刻被日本各大科技公司注意到,日本的三大运营商开始把Emoji加入到自己的短信业务中,很快便横扫了全日本,但为了打击竞争对手,各大运营商都使用自己的Emoji标准。这导致了不同运营商的手机无法正常显示对方手机发的Emoji

苹果是Emoji传遍全球的最大功臣。为了把iPhone打入日本市场,苹果决定在iOS 2.2中加入日本消费者的最爱emoji,为了迎合日本市场,他们在3个月的时间推出了400多个表情符号,极大地拓展了Emoji的表情数量,那时的iOS Emoji只在日本地区可用,但“好景不长”,北美的iOS 2.2用户发现了隐藏在系统中的Emoji,之后Emoji很快流行了起来,这种现象得到了其它科技公司的注意。

随着Emoji的流行,2010年,Emoji 首次被纳入Unicode v6.0字符集中。每个字符(表情)都被设定了统一且唯一的二进制码,从而保障了各平台手机都能使用Emoji,截止撰文期间,最新的Unicode v15.1字符集中已有3782个Emoji字符,而更新的Unicode v16版本预计于2024年9月发布release,届时会有更多的Emoji被支持。

Unicode

既然EmojiUnicode所收纳,那我们必先得去了解Unicode

广义上的Unicode是一个标准,定义了Unicode字符集以及一系列的编码规则,是一种收录了世界上所有语言的文字和符号的全球标准。

那么Unicode是怎样收录如此庞大的字符内容呢?很简单,给每个字符指定一个编号就行了,在Unicode中被称为码点CodePoint),它的表现形式为U+后面跟上一个十六进制数,比如U+0041表示大写字母A

世界上有那么多字符,Unicode并不是一次性定义的,而是分区定义,每个区可以存放 65536 (2^16) 个字符,称为一个平面(Plane),目前Unicode从第0平面到第16平面总共有17个平面,其中第0平面被称为基本平面(BMP),它的码点范围从0一直到65535,写成十六进制也就是 U+0000 - U+FFFF ,所有的常见字符都被放在这个平面,这是Unicode最先定义和公布的一个平面,而剩下的平面被称为辅助平面,码点范围从 U+010000 一直到 U+10FFFF

UTF-16

Unicode字符集只规定了每个字符的码点,但这一个个码点应该被计算机传输识别呢?这就涉及到编码的概念了,目前Unicode实际应用使用的编码方式为UCS-2,也就是每个字符占用2个字节,Unicode还有一种4字节的编码方式UCS-4,但这里不做讨论。

使用UCS-2编码方式包含65536个字符空间(2个字节的可用空间即为2^16),对应着表示着Unicode字符集中的基本平面,那剩余的辅助平面又该如何表示呢?UTF-16应运而生。

UTF-16UCS-2的超集,是一种变长编码,它的编码规则很简单:基本平面的字符占用2个字节,辅助平面的字符占用4个字节,也就是说UTF-16的编码长度要么是2个字节(U+0000-U+FFFF),要么是4个字节(U+010000-U+10FFFF)。

那么问题来了,采用UTF-16编码的时候,我们该怎么判断这个字符占用的是2个字节还是4个字节呢?这里有个巧妙的方式,在Unicode的基本平面中,从U+D800U+DFFF是一个空段,即这些码点不对应任何字符,因此,这个空段可以用来映射辅助平面的字符。辅助平面的字符一共有2^20个(一个平面2^16个字符 * 16个平面(2^4)),因此表示这些字符至少需要20个二进制位。UTF-16将这20个二进制位分成两半,前10位映射在U+D800U+DBFFUTF-16的高半区,空间大小2^10),称为高位(H),后10位映射在U+DC00U+DFFFUTF-16的低半区,空间大小2^10),称为低位(L)。这意味着,一个辅助平面的字符,被拆成两个基本平面的字符表示。

因此,每当程序遇到2个字节时,便会去判断它的码元是否在U+D800U+DBFF之间,如果在的话则可以假定它是一个4字节的字符,此时接着往后读2个字节,如果这2个字节的的码元在U+DC00U+DFFF之间,将他们组合起来获得到实际字符;而如果不在的话则可以判定为是一个2字节的字符。

我们以Java中获取码点的方法Character.codePointAt为例来解读一下代码中如何获取一个字符的码点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public final
class Character implements java.io.Serializable, Comparable<Character> {

// ...

public static final char MIN_HIGH_SURROGATE = '\uD800';
public static final char MAX_HIGH_SURROGATE = '\uDBFF';
public static final char MIN_LOW_SURROGATE = '\uDC00';
public static final char MAX_LOW_SURROGATE = '\uDFFF';

public static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x010000;

// ...

public static int codePointAt(CharSequence seq, int index) {
char c1 = seq.charAt(index);
// 码元在 U+D800 到 U+DBFF 之间,并且下一个char的index没到结尾
if (isHighSurrogate(c1) && ++index < seq.length()) {
char c2 = seq.charAt(index);
// 下一个char的码元在 U+DC00 到 U+DFFF 之间
if (isLowSurrogate(c2)) {
// 组合起来获得完整字符的码点
return toCodePoint(c1, c2);
}
}
// 字符码点即是单个char的码元
return c1;
}

// 码元是否在 U+D800 到 U+DBFF 之间
public static boolean isHighSurrogate(char ch) {
// Help VM constant-fold; MAX_HIGH_SURROGATE + 1 == MIN_LOW_SURROGATE
return ch >= MIN_HIGH_SURROGATE && ch < (MAX_HIGH_SURROGATE + 1);
}

// 码元是否在 U+DC00 到 U+DFFF 之间
public static boolean isLowSurrogate(char ch) {
return ch >= MIN_LOW_SURROGATE && ch < (MAX_LOW_SURROGATE + 1);
}

/**
* 计算规则:
* 1. 高位上的码元减掉高半区的起始值 0xD800 ,然后左移10位
* 2. 低位上的码元减掉低半区的起始值 0xDC00
* 3. 将 1 和 2 的计算结果以及辅助平面的起始值 0x010000 相加,获取到完整的码点值
*/
public static int toCodePoint(char high, char low) {
// Optimized form of:
// return ((high - MIN_HIGH_SURROGATE) << 10)
// + (low - MIN_LOW_SURROGATE)
// + MIN_SUPPLEMENTARY_CODE_POINT;
return ((high << 10) + low) + (MIN_SUPPLEMENTARY_CODE_POINT
- (MIN_HIGH_SURROGATE << 10)
- MIN_LOW_SURROGATE);
}

// ...

}

Emoji规则

了解了Unicode标准后,我们回过头来思考一下,是不是说EmojiUnicode标准中也仅仅只是被当成普通的字符看待呢?当然并非如此,除了之前说的平面规则等,EmojiUnicode中还有一套自己的规则,这些规则都可以在 Unicode 技术标准 #51 Emoji 中找到,官方的文档乍一看可能比较难理解,接下来就由我来给大家做一个解读。

首先,Emoji在大类上可以分成两种,一种是基本Emoji,一种是多字符组合而成的复合Emoji

基本Emoji

什么是基本Emoji呢?指的是直接在Unicode字符集里定义的一个Emoji字符,大多数基本Emoji字符都被划归到U+1F300-U+1F6FFU+1F900-U+1FAFF这两个区域

基本Emoji U+1F300-U+1F6FF

基本Emoji U+1F900-U+1FAFF

具体都有哪些基本Emoji字符,我们可以在Unicode的官网文档 emoji-data 中找到

复合Emoji

所谓的复合Emoji(我自己取的名字)指的是由多个字符组成的Emoji,它有着多种构造方式

Unicode 技术标准 #51 Emoji 中的1.4.9小节中,我们可以找到UnicodeEmoji定义的正则表达式,接下来我们就通过对这个正则表达式进行一步步的解析来了解Emoji的组成规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
\p{RI} \p{RI} 
| \p{Emoji}
( \p{EMod}
| \x{FE0F} \x{20E3}?
| [\x{E0020}-\x{E007E}]+ \x{E007F}
)?
(\x{200D}
( \p{RI} \p{RI}
| \p{Emoji}
( \p{EMod}
| \x{FE0F} \x{20E3}?
| [\x{E0020}-\x{E007E}]+ \x{E007F}
)?
)
)*

旗帜

首先我们看第一行的匹配条件\p{RI} \p{RI},这里的\p{RI}全称为Regional Indicator,翻译成中文就是区域指示符,根据这行正则我们可以了解到,两个区域指示符连接便可组成一个Emoji,那么这个区域指示符是什么呢?

通过 维基百科 我们可以得知,区域指示符指的是从U+1F1E6U+1F1FF中的字符,位于Unicode第一辅助平面的带圈字母数字补充区块内。

正如区块描述所说,这些字符看起来就像是一个个英文字母,外面套了个方框,这里是完整的字符表:🇦 🇧 🇨 🇩 🇪 🇫 🇬 🇭 🇮 🇯 🇰 🇱 🇲 🇳 🇴 🇵 🇶 🇷 🇸 🇹 🇺 🇻 🇼 🇽 🇾 🇿,通过两两组合的方式,将它们拼成国家或地区的代号,我们就能得到该国家或地区的旗帜。

以中国🇨🇳举例,中国的代号为CN,那我们就将🇨和🇳两个字符拼接到一起,便能得到中国国旗🇨🇳

肤色修饰符

第二行的\p{Emoji}指的就是我们之前说过的基本Emoji,这个是构成除旗帜外的复合Emoji的基础条件,接着我们看正则的第三到第六行,这里用括号括起了一个条件,括号的尾部跟了一个问号,表示括号中的这个条件最多只可以出现一次(0次或1次),括号内的条件又是由三个子条件组成,用或号分割,满足任意一条条件则视为整个条件成立,我们首先看第一个子条件\p{EMod}

全世界的人们都希望拥有反映更多人类多样性的Emoji,尤其是对于肤色。Unicode v8.0(2015年中)发行了五个为人类表情符号提供一系列肤色的符号修饰符符,具体的修饰符以及效果由下图所示:

肤色修饰符

我们以基本Emoji✋为例,它的码点是U+270B,在他后面加上U+1F3FB,这个Emoji就变成了✋🏻,同样的:

  • ✋ + U+1F3FC = ✋🏼
  • ✋ + U+1F3FD = ✋🏽
  • ✋ + U+1F3FE = ✋🏾
  • ✋ + U+1F3FF = ✋🏿

变体选择符

接着,我们再看第二个子条件x{FE0F} \x{20E3}?,这里的x{FE0F}指的是变体选择符-16Variation Selector-16),那么首先,什么是变体选择符呢?

实际上,支持象形文字的字体最早可以追溯到1993年,我们可以看一下Unicode字符集的装饰符号区U+2700-U+27FF

装饰符号区

那如果Emoji想要在这些象形文字的基础上做扩展,添加颜色,使其更加生动怎么办?没错,此时就需要使用到变体选择符了。

变体选择符(简称VS)是一个基本多文种平面的Unicode区段,包括16个变体选择符。这些选择器用于描述前一个字符的特点字形。目前 Unicode已定义数学符号、绘文字、八思巴字母及中日韩统一表意文字所对应的中日韩兼容表意文字。目前Unicode仅定义 VS1, VS2, VS3, VS15 及 VS16,VS15 和 VS16 分别用于标示某字符应该显示为普通文字或者是Emoji,这些字符被命名为U+FE00(VS1)至U+FE0F(VS16)。选择符仅应用于前一个字符。

以刚才我们在装饰符号区中看到的剪刀符号✂U+2702为例,在它的后面加上VS16 U+FE0F,这个字符就变成了Emoji✂️,是不是很神奇

键帽符

看完了变体选择符后,我们紧接着会疑惑,那这个条件后面的\x{20E3}又是啥呢?它被称为COMBINING ENCLOSING KEYCAP,它对前置的字符有一定的要求,只对数字、星号和井号生效,也就是说仅仅支持*#0123456789这12个字符。

它的规则是,当开头为这12个字符中的一个时,后面加上VS16 U+FE0F变成一个Emoji,然后在加上它U+20E3,这个字符就会变成一个键帽形状的字符:#️⃣ *️⃣ 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣

标签序列

然后是最后一个子条件[\x{E0020}-\x{E007E}]+ \x{E007F},这是一个标签序列,它由一个基础黑旗符号,一系列标签字符以及一个标签终止符组成,首先以基础黑旗符号🏴U+1F3F4开头,然后中间是一系列的U+E0020U+E007E之间的字符,最后以标签终止符U+E007F结尾,这样就组成了一个标签序列Emoji

目前这种Emoji不太常见,仅仅只有英格兰、苏格兰和威尔士的旗帜使用标签序列:

  • 🏴 + U+E0067 + U+E0062 + U+E0065 + U+E006E + U+E0067 + U+E007F = 🏴󠁧󠁢󠁥󠁮󠁧󠁿
  • 🏴 + U+E0067 + U+E0062 + U+E0073 + U+E0063 + U+E0074 + U+E007F = 🏴󠁧󠁢󠁳󠁣󠁴󠁿
  • 🏴 + U+E0067 + U+E0062 + U+E0077 + U+E006C + U+E0073 + U+E007F = 🏴󠁧󠁢󠁷󠁬󠁳󠁿

零宽度连接符

这些子条件看完,我们再回归到正则表达式中来,可以观察到8到14行的条件和1到6行的条件其实是完全一样的,而在第7行出现了一个条件\x{200D}连接了这两个一样的条件,什么意思呢?

没错,结合本文的起因部分,我们很容易的就可以联想到,这个\x{200D}起到的就是连接作用,它被称为零宽度连接符(ZERO-WIDTH JOINER,简称ZWJ),通过上面正则表达式尾部的*号我们可以得知,通过这个连接符,可以连接多个Emoji合成新的Emoji,下面举几个有趣的例子:

  • 👩U+1F469 + U+200D + ✈️U+2708 U+FE0F = 👩‍✈️
  • 👨U+1F468 + U+200D + 💻U+1F4BB = 👨‍💻
  • 🐻U+1F43B + U+200D + ❄️U+2744 U+FE0F = 🐻‍❄️
  • 🏴U+1F3F4 + U+200D + ☠️U+2620 U+FE0F = 🏴‍☠️
  • 🏳️U+1F3F3 U+FE0F + U+200D + 🌈U+1F308 = 🏳️‍🌈

以上是由两个Emoji组成一个新的Emoji的例子,而家庭以及人际关系相关的Emoji通常会由更多Emoji构成,就拿本文开头提到的例子👨‍👩‍👧‍👦,它的构成实际上是这样的:

👨U+1F468 + U+200D + 👩U+1F469 + 👧U+1F467 + 👦U+1F466 = 👨‍👩‍👧‍👦

这也就解释了在”达芬奇”流式打印文字的时候,为什么会依此显示👨👩👧👦四个Emoji,最后突然合成一整个👨‍👩‍👧‍👦了

Emoji字体

以上便是Emoji构成的所有规则了,但你有没有考虑过,一般字体都是黑白的矢量图形,为什么Emoji会显示成图片呢?

操作系统一般都会内置一种Emoji字体,MacOS/iOS内置的是Apple Color Emoji字体,Windows内置的是Segoe UI Emoji字体,Android内置的是Noto Color Emoji字体。这也是同一个Emoji再不同的设备上长得不一样的原因,除此之外,很多应用也会自带Emoji字体,比如WhatsAppTwitterFacebook

Apple Color Emoji

不同平台下的Emoji样式

回归初心,如何解决达芬奇流式打印问题

最后,让我们回到本文的出发点,了解了Emoji机制后,我们该如何解决”达芬奇”的流式打印问题呢?其实很简单,根据字符串向后做一个预测就可以了,啥叫预测?就是往后匹配,看这个字符的结构当前是否符合Emoji规则,以及加上后面的字符后有没有可能组成一个完整的Emoji,这里具体的代码我就不放了,给大家提供一个获取Emoji的代码吧,怎么预测可以通过这段代码自行感悟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
object EmojiUtils {

private const val VARIATION_SELECTOR_16 = 0xFE0F // 变体选择符-16
private const val COMBINING_ENCLOSING_KEY_CAP = 0x20E3 // 键帽符
private const val ZERO_WIDTH_JOINER = 0x200D // 零宽度连接符
private const val TERM_TAG = 0xE007F // 标签序列终止符

private const val KEY_CAP_START_CHARS = "#*0123456789" // 支持键帽符的所有字符

private val emojiModifierRange = 0x1F3FB..0x1F3FF // 肤色修饰符
private val tagModifierRange = 0xE0020..0xE007E // 标签序列
private val regionalIndicatorSymbolRange = 0x1F1E6..0x1F1FF // 区域指示符

private val basicEmojiSet = HashSet<Int>(4000) // 基本Emoji字符表

init {
basicEmojiSet.add(0x0023)
// ... 添加所有基本Emoji字符
// 使用 https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-data.txt 数据
}

/**
* 返回值不为null则代表该段字符是Emoji
*/
fun getEmoji(seq: CharSequence, start: Int, end: Int): CharSequence? {
if (start >= end) return null

var index = start
val firstCodePoint = Character.codePointAt(seq, index)
if (firstCodePoint >= 0x10000) {
index += 2
} else {
++index
}

// 判断是否为旗帜
if (isRegionalIndicatorSymbol(firstCodePoint)) {
if (index >= end) return null
val nextCodePoint = Character.codePointAt(seq, index)
return if (isRegionalIndicatorSymbol(nextCodePoint)) {
seq.subSequence(start, index + 2)
} else {
null
}
}

// 是否为基本Emoji
if (!isBasicEmoji(firstCodePoint)) return null

// 查找后面的肤色修饰符、变体选择符-16、键帽符以及标签序列,并返回整体长度
val modifierLength = getModifierLength(seq, index, end)
index += modifierLength
if (index >= end) {
return if (modifierLength == 0 && isKeyCapStartChars(firstCodePoint)) {
null
} else {
seq.subSequence(start, end)
}
}

val nextCodePoint = Character.codePointAt(seq, index)
if (modifierLength == 0 && nextCodePoint != ZERO_WIDTH_JOINER && isKeyCapStartChars(firstCodePoint)) {
return null
}

// 如果后一个字符是零宽度连接符,则递归查找下一个子Emoji
return if (nextCodePoint == ZERO_WIDTH_JOINER) {
//连接符占一个字符
index++
val afterEmoji = getEmoji(seq, index, end)
if (afterEmoji == null) {
null
} else {
seq.subSequence(start, index + afterEmoji.length)
}
} else {
seq.subSequence(start, index)
}
}

/**
* 旗帜Emoji
*/
private fun isRegionalIndicatorSymbol(codePoint: Int): Boolean {
return codePoint in regionalIndicatorSymbolRange
}

private fun isBasicEmoji(codePoint: Int): Boolean {
// 其实这里应该可以通过以下方法判断,但由于该方法的最低Api限制是Android N,
// 在项目中无法使用,所以这里通过自己添加基本Emoji的方式手动判断
// UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI)
return codePoint in basicEmojiSet
}

private fun getModifierLength(seq: CharSequence, start: Int, end: Int): Int {
if (start >= end) return 0

var index = start

var codePoint = Character.codePointAt(seq, index)

//肤色
if (codePoint in emojiModifierRange) return 2

if (codePoint == VARIATION_SELECTOR_16) {
return if (index + 2 < end) {
val nextCodePoint = Character.codePointAt(seq, index + 2)
if (nextCodePoint == COMBINING_ENCLOSING_KEY_CAP) {
3
} else {
2
}
} else {
2
}
}

while (codePoint in tagModifierRange) {
index += 2
if (index >= end) break
codePoint = Character.codePointAt(seq, index)
}

return if (index < end && index != start && codePoint == TERM_TAG) {
index - start
} else {
0
}
}

private fun isKeyCapStartChars(codePoint: Int): Boolean {
return KEY_CAP_START_CHARS.contains(codePoint.toChar())
}
}

参考文献

工具


2023小红书Android面试之旅

一面

  • 自我介绍

  • 看你写了很多文章,拿你理解最深刻的一篇出来讲一讲

    讲了Binder相关内容

  • Binder大概分了几层

  • 哪些方法调用会涉及到Binder通信

  • 大概讲一下startActivity的流程,包括与AMS的交互

  • 全页面停留时长埋点是怎么做的

    我在项目中做过的内容,主要功能是计算用户在每个Activity的停留时长,并且支持多进程。这里的多进程支持主要是通过以ContentProvider作为中介,然后通过ContentResolver.call方法去调用它的各种方法以实现跨进程

  • 动态权限申请是什么

    详见 Android动态权限申请从未如此简单 这篇文章

  • 你做的性能监测工具,FPS是怎么采集的

  • 性能监测工具用在了什么场景

  • 有没有通过这个性能监测工具去做一些优化

  • 图片库,例如Glide,一般对Bitmap有哪些优化点

  • 过期的Bitmap可以复用吗

  • 有没有基于ASM插桩做过一些插件

  • 讲了一下当时做过的一个个人项目 FastInflate

    这个项目没能达到最终的目标,但通过做这个项目学习了很多新知识,比如APT代码生成、阅读了LayoutInflater源码、AppCompatDelegateImpl实现的LayoutInflater.Factory2会极大的拖慢布局创建的速度等

  • 怎么优化布局创建速度

    提示了预加载,但我当时脑抽在纠结xml的缓存,没想到可以提前把视图先创建好

  • 说一下你觉得你最擅长或者了解最透的点

    我回答的自定义View

  • 解决过View的滑动冲突吗

  • 讲解了一个之前写过的开源控件 SwipeLoadingLayout

  • 一般遇到困难的解决方案是什么

  • 算法题:反转链表

  • 反问阶段

    • 咱们组主要负责哪些内容

    • 主要使用Java还是Kotlin

      Kotlin

    • 小红书的面试一般是怎么个流程?多少轮?

      一般三轮技术面,一轮HR面

    • 面试完一般多久会给到结果

      比较快,一两天的样子

二面

  • 自我介绍

  • 为什么这个时间节点想要出来换工作呢

  • 在B站这些年做了什么

  • 做了哪些基础组件

    讲解了一下之前写的 SwipeLoadingLayout

  • 介绍一下Android的事件传递机制

  • 你写的这个分享模块是如何设计的

    对外采用流式调用的形式,内部通过策略模式区分不同的平台以及分享类型,给每个平台创建了一个中间Activity作为分享SDK请求的发起方(SDK.getApi().share())以及分享结果的接收方(onActivityResult),然后通过广播将分享的结果送入到分享模块内进行处理,最终调用用户设置的分享回调告知结果

  • 看你之前在扇贝的时候有开发过一些性能监测工具,那有做过性能优化吗

  • 你是如何收集这些性能数据的

  • 有没有对哪方面做过一些针对性的优化

  • Android系统为什么会触发ANR,它的机制是什么

  • 有解过ANR相关的问题吗?有哪几种类型?

  • 算法题:二叉树的层序遍历

  • Queue除了LinkedList还有哪些实现类

  • 现在还在面其他公司吗?你自己后面职业生涯的选择是怎么样的?

  • 给我介绍了一下团队,说我面试的这个部门应该说是小红书最核心的团队,包括主页、搜索、图文、视频等等都在部门业务范畴内,部门主要分三层,除了业务层之外还有基础架构层以及性能优化层

  • 反问阶段

    • 部门分三层的话,那新人进来的话是需要从业务层做起吗?

      不是这样的,我们首先会考虑这个同学能干什么,然后会考虑这个同学愿意去做什么,进来后,有经验的同学也会来带你的,不会一上来就让你抗输出,总之会把人放到适合他的团队里

    • 小红书会使用到一些跨端技术吗?

      会,之前在一些新的App上使用的Flutter,现在主要用的是RN,还会使用到一些DSL,这个不能算跨段。为什么在小红书社区App中跨端技术提及的比较少,是因为小红书App非常重视用户体验,对性能的要求比较高

三面

  • 自我介绍

  • 介绍一下目前负责的业务

  • 工作过程中有碰到过什么难题,最后是怎么解决的

    一开始脑抽了没想到该说什么,随便扯了一个没啥技术含量的东西,又扯了一个之前做的信号捕获的工具,后来回忆起来了,重新说了一个关于DEX编排的东西(主DEX中方法数超过65535导致打包失败,写了个脚本将一部分Class从主DEX中移除到其他DEX中)

  • 如何设计一个头像的自定义View,要求使头像展示出来是一个圆形

  • 介绍一下Android事件的分发流程

  • 如何处理View的防误触

  • 怎么处理滑动冲突

  • ActivityonCreate方法中调用了finish方法,那它的生命周期会是怎样的

  • 如果我想判断一个Activity中的一个View的尺寸,那我什么时候能够拿到

  • RecyclerView如何实现一个吸顶效果

  • JavaKoltin你哪个用的比较多

  • 有用过Kotlin的协程吗

  • Kotlin中的哪些Feature你用的多,觉得写的好呢

  • 你是怎么理解MVVM

  • 你有用过Jetpack Compose

  • 有用过kotlin中的by lazylateinit

  • kotlin中怎么实现单例,怎么定义一个类的静态变量

  • 算法题:增量元素之间的最大差值

  • 你这次看机会的原因是什么

  • 反问阶段我感觉之前问的差不多了,这次就没再问什么问题了

HR面

  • 现在是离职还是在职状态

  • 介绍一下之前负责的工作

  • 用户量怎么样

  • 这个项目是从0到1开发的吗

  • 这个业务有什么特点,对于客户端开发有什么挑战与困难

  • 团队分工是怎样的

  • 这个项目能做成现在这个样子,你自己的核心贡献有哪些

  • 这个事情对你来说有什么收获吗

  • 在B站的工作节奏是怎么样的

  • 离职的原因是什么呢

  • 你自己希望找一个什么样的环境或者什么阶段的业务

  • 你对小红书有什么了解吗

  • 未来两三年对于职业发展的想法

  • 你觉得现在有什么限制了你或者你觉得你需要提升哪些部分

  • 反问阶段

    • 问了一些作息、福利待遇之类的问题

总结

小红书面试总体而言给我的体验是很好的,每轮面试后基本上都是当天就能出结果,然后约下一轮的面试。最终从一面到HR面结束出结果,一共花了9天时间,还是挺快的。二面结束后,一面的面试官加我微信说小红书目前很缺人,感兴趣的同学也可以来试试。


Android源码分析 - Service的停止与重建

开篇

本篇以android-11.0.0_r25作为基础解析

在上一篇文章 Android源码分析 - Service启动流程 中,我们分析了一个Service是怎么启动的,这次我们再来看看一个Service是如何被停止的,什么情况下Service会被重建以及它的重建过程

流程图

由于Service的停止与重建在实际情况下会收到各种条件影响产生不同的情况,所以这里的流程图我也只画最简路径,以供大家参考

service主动停止流程图

service主动解绑流程图

Task移除流程图

service重建流程图

主动停止

首先,我们来看主动停止的情况,主动停止也分三种:

  • Service自己调用stopSelfstopSelfResult方法停止

  • 使用startService启动的Service,使用stopService方法停止

  • 使用bindService启动的Service,使用unbindService方法解除绑定,当没有任何ClientService绑定时,Service就会自行停止

Service.stopSelf 或 Service.stopSelfResult

stopSelfstopSelfResult方法唯一的区别是一个没返回值,一个会返回是否成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//frameworks/base/core/java/android/app/Service.java
public final void stopSelf() {
stopSelf(-1);
}

public final void stopSelf(int startId) {
if (mActivityManager == null) {
return;
}
try {
mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
}

public final boolean stopSelfResult(int startId) {
if (mActivityManager == null) {
return false;
}
try {
return mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
return false;
}

这个方法也是直接调用了AMS.stopServiceToken方法

1
2
3
4
5
6
7
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public boolean stopServiceToken(ComponentName className, IBinder token,
int startId) {
synchronized(this) {
return mServices.stopServiceTokenLocked(className, token, startId);
}
}

然后AMS转手调用了ActiveServices.stopServiceTokenLocked方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
boolean stopServiceTokenLocked(ComponentName className, IBinder token,
int startId) {
//通过className查找相应的ServiceRecord
//在Service启动过程中调用的retrieveServiceLocked方法会查找Service,创建ServiceRecord
//并将其添加到Map中,findServiceLocked方法只需要从这个Map中去获取即可
ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
if (r != null) {
if (startId >= 0) {
// Asked to only stop if done with all work. Note that
// to avoid leaks, we will take this as dropping all
// start items up to and including this one.
//查找startId所对应的已分发的启动项
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false);
//从已分发启动请求列表中移除
if (si != null) {
while (r.deliveredStarts.size() > 0) {
ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
cur.removeUriPermissionsLocked();
if (cur == si) {
break;
}
}
}

//如果传入的启动ID不是Service最后一次启动的ID,则不能停止服务
//ps:每次启动Service,startId都会递增,初始值为1
if (r.getLastStartId() != startId) {
return false;
}
}

... //记录
//重置启动状态(重要,后文中会分析)
r.startRequested = false;
r.callStart = false;
final long origId = Binder.clearCallingIdentity();
//接着停止Service
bringDownServiceIfNeededLocked(r, false, false);
Binder.restoreCallingIdentity(origId);
return true;
}
return false;
}

startId机制

这里要说一下ServicestartId机制,每次启动Service时,ActiveServices.startServiceLocked方法会向ServiceRecord.pendingStarts列表中添加一个启动项ServiceRecord.StartItem,构建这个启动项需要提供一个startId,而这个startId是由ServiceRecord.makeNextStartId生成的

1
2
3
4
5
6
7
8
//frameworks/base/services/core/java/com/android/server/am/ServiceRecord.java
public int makeNextStartId() {
lastStartId++;
if (lastStartId < 1) {
lastStartId = 1;
}
return lastStartId;
}

由于lastStartId的初始值为 0 ,所以第一次调用这个方法,得到的startId就是 1 ,即startId是从 1 开始递增的,由于当Service被停止后,ServiceRecord会从之前的缓存Map中移除,所以下一次再启动Service时会重新创建ServiceRecordstartId会被重置

当我们调用stopSelf停止服务时,如果传入了大于等于 0 的startId,此时便会判断这个startId是不是最后一次启动所对应的startId,如果不是的话,则不能停止这个Service

这个startId设计的意义是什么呢?我们从IntentService的设计中可以管中窥豹:

IntentService是一个运行在另一个线程的,每次处理完任务都会自动停止的Service,但如果你调用多次startService会发现,IntentService.onDestroy方法只会调用一次,这是为什么呢?因为IntentService每次停止,调用stopSelf方法都是带上这次启动的startId的,这样如果一次性有多个启动请求,前面的任务执行完,停止时发现,此次启动请求的startId不是最后一个startId,这样就不会停止掉自身,直到最后一个任务处理完成,避免了Service的多次停止启动消耗系统资源


接着,在这个方法的最后,调用bringDownServiceIfNeededLocked方法继续停止服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn,
boolean hasConn) {
//检查此服务是否还被需要
//在用stopSelf停止服务的这种情况下
//检查的就是是否有auto-create的连接(flag为BIND_AUTO_CREATE)
//如有则不能停止服务
if (isServiceNeededLocked(r, knowConn, hasConn)) {
return;
}

// Are we in the process of launching?
//不要停止正在启动中的Service
if (mPendingServices.contains(r)) {
return;
}

//继续停止服务
bringDownServiceLocked(r);
}

这个方法主要就做了一些能否停止服务的检查,主要的停止操作都在下一个bringDownServiceLocked方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
private final void bringDownServiceLocked(ServiceRecord r) {
... //处理Client与Serivce的连接,进行断开连接以及解除绑定操作

// Check to see if the service had been started as foreground, but being
// brought down before actually showing a notification. That is not allowed.
//如果此Service是以前台服务的形式启动,并且当前还尚未成为前台服务
if (r.fgRequired) {
r.fgRequired = false;
r.fgWaiting = false;
... //记录
//将前台服务的超时回调取消
mAm.mHandler.removeMessages(
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
//这种情况直接令App崩溃,杀死应用
if (r.app != null) {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
msg.obj = r.app;
msg.getData().putCharSequence(
ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
mAm.mHandler.sendMessage(msg);
}
}

//记录销毁时间
r.destroyTime = SystemClock.uptimeMillis();

//从缓存中移除ServiceRecord
final ServiceMap smap = getServiceMapLocked(r.userId);
ServiceRecord found = smap.mServicesByInstanceName.remove(r.instanceName);

// Note when this method is called by bringUpServiceLocked(), the service is not found
// in mServicesByInstanceName and found will be null.
if (found != null && found != r) {
// This is not actually the service we think is running... this should not happen,
// but if it does, fail hard.
//如果找到的服务不是我们目前停止的服务,应该是一个不可能的情况
//碰到这种情况,将ServiceRecord重新放回去并抛出异常
smap.mServicesByInstanceName.put(r.instanceName, found);
throw new IllegalStateException("Bringing down " + r + " but actually running "
+ found);
}
//清除ServiceRecord
smap.mServicesByIntent.remove(r.intent);
r.totalRestartCount = 0;
//取消之前的Service重启任务(如果有)
unscheduleServiceRestartLocked(r, 0, true);

// Also make sure it is not on the pending list.
//从待启动Service列表中移除
for (int i=mPendingServices.size()-1; i>=0; i--) {
if (mPendingServices.get(i) == r) {
mPendingServices.remove(i);
}
}

//关闭前台服务通知
cancelForegroundNotificationLocked(r);
//对于已经成为前台服务的Service
if (r.isForeground) {
//修改应用的活动前台计数,如果计数小于等于0,将其从mActiveForegroundApps列表中移除
decActiveForegroundAppLocked(smap, r);
... //更新统计信息
}

//各种清理操作
r.isForeground = false;
r.foregroundId = 0;
r.foregroundNoti = null;
r.mAllowWhileInUsePermissionInFgs = false;

// Clear start entries.
r.clearDeliveredStartsLocked();
r.pendingStarts.clear();
smap.mDelayedStartList.remove(r);

if (r.app != null) {
synchronized (r.stats.getBatteryStats()) {
r.stats.stopLaunchedLocked();
}
//从ProcessRecord中移除Service记录
r.app.stopService(r);
//更新服务进程绑定的应用uids
r.app.updateBoundClientUids();
//允许绑定Service的应用程序管理白名单
if (r.whitelistManager) {
updateWhitelistManagerLocked(r.app);
}
if (r.app.thread != null) {
//更新进程前台服务信息
updateServiceForegroundLocked(r.app, false);
try {
//记录Service执行操作并设置超时回调
//前台服务超时时间为20s,后台服务超时时间为200s
bumpServiceExecutingLocked(r, false, "destroy");
//添加到销毁中Service列表中
mDestroyingServices.add(r);
//标记正在销毁中
r.destroying = true;
//更新进程优先级
mAm.updateOomAdjLocked(r.app, true,
OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
//回到App进程,调度执行Service的stop操作
r.app.thread.scheduleStopService(r);
} catch (Exception e) {
serviceProcessGoneLocked(r);
}
}
}

//清除连接
if (r.bindings.size() > 0) {
r.bindings.clear();
}

//对于主动停止的Service,不需要重启
if (r.restarter instanceof ServiceRestarter) {
((ServiceRestarter)r.restarter).setService(null);
}

... //记录

//将此Service从正在后台启动服务列表和延迟启动服务列表中移除
//如果正在后台启动服务列表中存在此服务的话,将之前设置为延迟启动的服务调度出来后台启动
smap.ensureNotStartingBackgroundLocked(r);
}

这个方法主要做了以下几件事:

  1. 处理ClientSerivce的连接,进行断开连接以及解除绑定操作(具体等到后面分析unbindService时再说)
  2. 对于以前台服务形式启动,并且当前还尚未成为前台服务的Service,直接杀死App
  3. 各种重置,清理操作
  4. 关闭前台服务通知
  5. Service添加到销毁中服务列表,并调度执行停止操作,最终回调Service.onDestroy
  6. ServiceRestarter内的ServiceRecord变量设为null,避免其后续重启

我们重点看第5步,在这个方法中调用了ActivityThread$ApplicationThread.scheduleStopService去调度执行停止服务操作

1
2
3
4
//frameworks/base/core/java/android/app/ActivityThread.java
public final void scheduleStopService(IBinder token) {
sendMessage(H.STOP_SERVICE, token);
}

同样的,也是通过Handler发送Message处理服务停止,这里最终调用的是ActivityThread.handleStopService方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//frameworks/base/core/java/android/app/ActivityThread.java
private void handleStopService(IBinder token) {
//从Service列表中取出并移除此服务
Service s = mServices.remove(token);
if (s != null) {
try {
//调用Service.onDestroy
s.onDestroy();
//解绑以及清理(解除对ServiceRecord的Binder远程对象的引用)
s.detachAndCleanUp();
//执行清理操作,具体来说就是断开其他客户端与Service的连接以及解除绑定
Context context = s.getBaseContext();
if (context instanceof ContextImpl) {
final String who = s.getClassName();
((ContextImpl) context).scheduleFinalCleanup(who, "Service");
}

//确保其他异步任务执行完成
QueuedWork.waitToFinish();

try {
//Service相关任务执行完成
//这一步中会把之前的超时定时器取消
ActivityManager.getService().serviceDoneExecuting(
token, SERVICE_DONE_EXECUTING_STOP, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to stop service " + s
+ ": " + e.toString(), e);
}
}
}
}

可以看到,这里首先从mServices列表中取出并移除此服务,然后触发Service.onDestroy回调,之后还需要调用ContextImpl.scheduleFinalCleanup方法执行一些清理工作,这一部分的分析我们留到后面unbindService章节里再讲,这样,整个Service的停止流程就到此结束了

stopService

接下来我们来看一下调用方调用stopService停止服务的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//frameworks/base/core/java/android/app/ContextImpl.java
public boolean stopService(Intent service) {
warnIfCallingFromSystemProcess();
return stopServiceCommon(service, mUser);
}

private boolean stopServiceCommon(Intent service, UserHandle user) {
try {
//验证Intent有效性
validateServiceIntent(service);
//跨进程处理
service.prepareToLeaveProcess(this);
//调用AMS.stopService
int res = ActivityManager.getService().stopService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to stop service " + service);
}
return res != 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}

这里基本上是直接调用AMS.stopService进入系统进程处理服务停止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public int stopService(IApplicationThread caller, Intent service,
String resolvedType, int userId) {
enforceNotIsolatedCaller("stopService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}

synchronized(this) {
//转交给ActiveServices处理
return mServices.stopServiceLocked(caller, service, resolvedType, userId);
}
}

//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
int stopServiceLocked(IApplicationThread caller, Intent service,
String resolvedType, int userId) {
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (caller != null && callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when stopping service " + service);
}

// If this service is active, make sure it is stopped.
//查找相应的Service,其中入参createIfNeeded为false,所以如果从缓存中找不到ServiceRecord的话则会直接返回null
ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, null,
Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false, false);
if (r != null) {
if (r.record != null) {
final long origId = Binder.clearCallingIdentity();
try {
//接着处理停止服务
stopServiceLocked(r.record);
} finally {
Binder.restoreCallingIdentity(origId);
}
return 1;
}
return -1;
}

return 0;
}

private void stopServiceLocked(ServiceRecord service) {
if (service.delayed) {
// If service isn't actually running, but is being held in the
// delayed list, then we need to keep it started but note that it
// should be stopped once no longer delayed.
service.delayedStop = true;
return;
}
... //统计信息记录

//重置启动状态(重要,后文中会分析)
service.startRequested = false;
service.callStart = false;

//和上文一样,停止Service
bringDownServiceIfNeededLocked(service, false, false);
}

可以看到,这里和上文中分析的以stopSelf方式停止服务一样,先重置启动状态,然后调用bringDownServiceIfNeededLocked停止服务

unbindService

接下来的是通过bindService方法,并且flagBIND_AUTO_CREATE启动的Service,我们需要通过unbindService方法解除绑定,当最终没有任何flagBIND_AUTO_CREATE的客户端与Service绑定,这个Service就会被停止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//frameworks/base/core/java/android/app/ContextImpl.java
public void unbindService(ServiceConnection conn) {
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (mPackageInfo != null) {
//将ServiceDispatcher的状态设置为forgotten,之后便不再会回调ServiceConnection任何方法
IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
getOuterContext(), conn);
try {
//调用AMS.unbindService方法
ActivityManager.getService().unbindService(sd);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
throw new RuntimeException("Not supported in system context");
}
}

这个方法中首先调用了LoadedApk.forgetServiceDispatcher方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//frameworks/base/core/java/android/app/LoadedApk.java
public final IServiceConnection forgetServiceDispatcher(Context context,
ServiceConnection c) {
synchronized (mServices) {
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map
= mServices.get(context);
LoadedApk.ServiceDispatcher sd = null;
if (map != null) {
sd = map.get(c);
if (sd != null) {
//移除ServiceDispatcher
map.remove(c);
//清理连接并标记遗忘
sd.doForget();
if (map.size() == 0) {
mServices.remove(context);
}
... //debug
return sd.getIServiceConnection();
}
}
... //debug
... //异常
}
}

ServiceDispatcher从缓存中移除并调用LoadedApk$ServiceDispatcher.doForget方法

1
2
3
4
5
6
7
8
9
10
11
//frameworks/base/core/java/android/app/LoadedApk.java
void doForget() {
synchronized(this) {
for (int i=0; i<mActiveConnections.size(); i++) {
ServiceDispatcher.ConnectionInfo ci = mActiveConnections.valueAt(i);
ci.binder.unlinkToDeath(ci.deathMonitor, 0);
}
mActiveConnections.clear();
mForgotten = true;
}
}

这里将所有连接的binder死亡回调移除,然后清除所有连接,再将mForgotten标记设为true

接着我们会走到AMS.unbindService方法中

1
2
3
4
5
6
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public boolean unbindService(IServiceConnection connection) {
synchronized (this) {
return mServices.unbindServiceLocked(connection);
}
}

同样的,将工作转交给ActiveServices

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
boolean unbindServiceLocked(IServiceConnection connection) {
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
//找不到连接记录,直接返回
if (clist == null) {
return false;
}

final long origId = Binder.clearCallingIdentity();
try {
//遍历连接
while (clist.size() > 0) {
ConnectionRecord r = clist.get(0);
//移除连接
removeConnectionLocked(r, null, null);
//removeConnectionLocked方法会将此ConnectionRecord从连接列表中移除
//如果此ConnectionRecord仍然存在的话,是一个严重的错误,这里再移除一次
if (clist.size() > 0 && clist.get(0) == r) {
// In case it didn't get removed above, do it now.
Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder);
clist.remove(0);
}

if (r.binding.service.app != null) {
if (r.binding.service.app.whitelistManager) {
updateWhitelistManagerLocked(r.binding.service.app);
}
// This could have made the service less important.
if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
r.binding.service.app.treatLikeActivity = true;
mAm.updateLruProcessLocked(r.binding.service.app,
r.binding.service.app.hasClientActivities()
|| r.binding.service.app.treatLikeActivity, null);
}
}
}

//更新顶层应用进程优先级
mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);

} finally {
Binder.restoreCallingIdentity(origId);
}

return true;
}

没看到什么特别重要的逻辑,看来重点应该在removeConnectionLocked这个方法中了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
ActivityServiceConnectionsHolder skipAct) {
IBinder binder = c.conn.asBinder();
AppBindRecord b = c.binding;
ServiceRecord s = b.service;
//这里的clist是ServiceRecord中的列表,和上一个方法中的clist不是一个对象
ArrayList<ConnectionRecord> clist = s.getConnections().get(binder);
//移除各种连接
if (clist != null) {
clist.remove(c);
if (clist.size() == 0) {
s.removeConnection(binder);
}
}
b.connections.remove(c);
c.stopAssociation();
if (c.activity != null && c.activity != skipAct) {
c.activity.removeConnection(c);
}
if (b.client != skipApp) {
b.client.connections.remove(c);
... //各种flag的处理
//更新是否有与Service建立连接的Activity
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
}
}
//将连接从mServiceConnections列表中移除
//这个clist才是和上一个方法是同一个对象
clist = mServiceConnections.get(binder);
if (clist != null) {
clist.remove(c);
if (clist.size() == 0) {
mServiceConnections.remove(binder);
}
}

mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid,
s.appInfo.longVersionCode, s.instanceName, s.processName);

//如果调用方App没有其他连接和Service绑定
//则将整个AppBindRecord移除
if (b.connections.size() == 0) {
b.intent.apps.remove(b.client);
}

if (!c.serviceDead) {
//如果服务端进程存活并且没有其他连接绑定了,同时服务还处在绑定关系中(尚未回调过Service.onUnbind)
if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
&& b.intent.hasBound) {
try {
bumpServiceExecutingLocked(s, false, "unbind");
if (b.client != s.app && (c.flags&Context.BIND_WAIVE_PRIORITY) == 0
&& s.app.setProcState <= ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
// If this service's process is not already in the cached list,
// then update it in the LRU list here because this may be causing
// it to go down there and we want it to start out near the top.
mAm.updateLruProcessLocked(s.app, false, null);
}
mAm.updateOomAdjLocked(s.app, true,
OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
//标记为未绑定
b.intent.hasBound = false;
// Assume the client doesn't want to know about a rebind;
// we will deal with that later if it asks for one.
b.intent.doRebind = false;
//回到App进程,调度执行Service的unbind操作
s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
} catch (Exception e) {
serviceProcessGoneLocked(s);
}
}

// If unbound while waiting to start and there is no connection left in this service,
// remove the pending service
if (s.getConnections().isEmpty()) {
mPendingServices.remove(s);
}

if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
//是否有其他含有BIND_AUTO_CREATE标记的连接
boolean hasAutoCreate = s.hasAutoCreateConnections();
... //记录
bringDownServiceIfNeededLocked(s, true, hasAutoCreate);
}
}
}

这个方法主要做了以下几件事:

  1. 执行各种移除操作

  2. 如果目标服务在此次解绑后不再有任何其他连接与其绑定,则调度执行Serviceunbind操作

  3. 如果此次断开的连接的flag中包含BIND_AUTO_CREATE,则调用bringDownServiceIfNeededLocked尝试停止服务

2、3两点都很重要,我们首先看第2点,什么情况下会在这里调度执行Serviceunbind操作,前面描述的其实不是很准确,准确的来说应该是目标服务在同一个IntentBindRecord下,此次解绑后不再有任何其他连接与其绑定。那么什么叫同一个IntentBindRecord呢?这和我们启动服务传入的Intent有关,IntentBindRecord的第一次创建是在我们调用bindService后,走到ActiveServices.bindServiceLocked方法中,其中有一段代码调用了ServiceRecord.retrieveAppBindingLocked方法产生的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//frameworks/base/services/core/java/com/android/server/am/ServiceRecord.java
public AppBindRecord retrieveAppBindingLocked(Intent intent,
ProcessRecord app) {
Intent.FilterComparison filter = new Intent.FilterComparison(intent);
IntentBindRecord i = bindings.get(filter);
if (i == null) {
i = new IntentBindRecord(this, filter);
bindings.put(filter, i);
}
AppBindRecord a = i.apps.get(app);
if (a != null) {
return a;
}
a = new AppBindRecord(this, i, app);
i.apps.put(app, a);
return a;
}

在这个方法中,它将我们传入的Intent包装成了Intent.FilterComparison对象,然后尝试用它作为keyArrayMap``bindings中取获取IntentBindRecord,如果获取不到则会创建一个新的,那么是否是同一个IntentBindRecord的判断标准就是包装后的Intent.FilterComparison对象的HashCode是否相等,我们来看一下它的HashCode是怎样计算的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//frameworks/base/core/java/android/content/Intent.java
public static final class FilterComparison {
private final Intent mIntent;
private final int mHashCode;

public FilterComparison(Intent intent) {
mIntent = intent;
mHashCode = intent.filterHashCode();
}
...
@Override
public int hashCode() {
return mHashCode;
}
}

public int filterHashCode() {
int code = 0;
if (mAction != null) {
code += mAction.hashCode();
}
if (mData != null) {
code += mData.hashCode();
}
if (mType != null) {
code += mType.hashCode();
}
if (mIdentifier != null) {
code += mIdentifier.hashCode();
}
if (mPackage != null) {
code += mPackage.hashCode();
}
if (mComponent != null) {
code += mComponent.hashCode();
}
if (mCategories != null) {
code += mCategories.hashCode();
}
return code;
}

可以看到,只有以上参数全部相等,才会被视为同一个Intent,而我们通常使用Intent只会设置它的mComponent,所以在一般情况下ServiceonBindonUnbind也只会触发一次(在Service没有被销毁的情况下)

接着我们来看第3点,如果此次断开的连接的flag中包含BIND_AUTO_CREATE,首先会去查询是否有其他含有BIND_AUTO_CREATE标记的连接,然后以此作为参数调用bringDownServiceIfNeededLocked尝试停止服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn,
boolean hasConn) {
//检查此服务是否还被需要
if (isServiceNeededLocked(r, knowConn, hasConn)) {
return;
}

// Are we in the process of launching?
//不要停止正在启动中的Service
if (mPendingServices.contains(r)) {
return;
}

//继续停止服务
bringDownServiceLocked(r);
}

private final boolean isServiceNeededLocked(ServiceRecord r, boolean knowConn,
boolean hasConn) {
// Are we still explicitly being asked to run?
//Service之前是否通过startService启动过并且未stop
if (r.startRequested) {
return true;
}

// Is someone still bound to us keeping us running?
//这里我们传入的是true
//因为我们之前已经做了检查,知道了是否还有其他auto-create的连接
if (!knowConn) {
hasConn = r.hasAutoCreateConnections();
}
//如果还有其他auto-create的连接
//则此服务还被需要
if (hasConn) {
return true;
}

return false;
}

可以看到,经过上述检查,如果发现此Service确实可以被停止了,则会调用bringDownServiceLocked方法停止服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
private final void bringDownServiceLocked(ServiceRecord r) {
//断开所有连接
ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> c = connections.valueAt(conni);
for (int i=0; i<c.size(); i++) {
ConnectionRecord cr = c.get(i);
// There is still a connection to the service that is
// being brought down. Mark it as dead.
//将服务标记为死亡
cr.serviceDead = true;
cr.stopAssociation();
//回调ServiceConnection各种方法
//通知client服务断开连接以及死亡
cr.conn.connected(r.name, null, true);
}
}

// Tell the service that it has been unbound.
//通知Service解除绑定
if (r.app != null && r.app.thread != null) {
boolean needOomAdj = false;
//遍历所有连接,解除绑定
for (int i = r.bindings.size() - 1; i >= 0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
//如果还处在绑定关系中(尚未回调过Service.onUnbind)
if (ibr.hasBound) {
try {
//记录Service执行操作并设置超时回调
//前台服务超时时间为20s,后台服务超时时间为200s
bumpServiceExecutingLocked(r, false, "bring down unbind");
needOomAdj = true;
//标记为未绑定
ibr.hasBound = false;
ibr.requested = false;
//回到App进程,调度执行Service的unbind操作
r.app.thread.scheduleUnbindService(r,
ibr.intent.getIntent());
} catch (Exception e) {
needOomAdj = false;
serviceProcessGoneLocked(r);
break;
}
}
}
//更新服务端进程优先级
if (needOomAdj) {
mAm.updateOomAdjLocked(r.app, true,
OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
}
}
... //和上文相同
}

这个方法我们在前面分析stopSelf的时候说过了,这次我们只看和绑定服务有关的部分

首先断开所有连接,回调ServiceConnection各种方法,通知客户端服务断开连接以及死亡,这里需要注意的是,我们本次执行unbindService操作的连接已经在上一步中从ServiceRecord.connections列表中移除,所以并不会回调它的ServiceConnection的任何方法,这也是很多人对unbindService方法的误解(包括我自己),bindService方法在成功绑定服务后会回调ServiceConnection.onServiceConnected方法,但unbindService方法在成功解绑服务后并不会回调ServiceConnection.onServiceDisconnected以及任何其它方法,这些方法只会在Service被以其他方式停止(比如后面会分析的混合启动的服务如何停止)或者Service意外停止(比如服务端应用崩溃或被杀死)的情况才会被调用

所以这里处理的是断开其他的连接,我们假设一个场景,使用同一个Intent和两个不同的ServiceConnection,一个使用BIND_AUTO_CREATE标记,一个使用其他标记,先绑定BIND_AUTO_CREATE标记的Service,然后再绑定其他标记的Service,接着我们对BIND_AUTO_CREATE标记的Serivce调用unbindService解绑,此时就会走到这个方法中,ServiceRecord.connections列表中会存在那个使用其他标记的连接,然后其内部成员变量connconnected方法,这个conn是一个IServiceConnection类型,实际上的实现类为LoadedApk$ServiceDispatcher$InnerConnection,最终会调用到LoadedApk$ServiceDispatcher.doConnected方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public void doConnected(ComponentName name, IBinder service, boolean dead) {
ServiceDispatcher.ConnectionInfo old;
ServiceDispatcher.ConnectionInfo info;

synchronized (this) {
//被标记为遗忘则不处理任何事情
//调用unbindService就会将这个标志设为true
if (mForgotten) {
// We unbound before receiving the connection; ignore
// any connection received.
return;
}
old = mActiveConnections.get(name);
//如果旧的连接信息中的IBinder对象和本次调用传入的IBinder对象是同一个对象
if (old != null && old.binder == service) {
// Huh, already have this one. Oh well!
return;
}

if (service != null) {
// A new service is being connected... set it all up.
//建立一个新的连接信息
info = new ConnectionInfo();
info.binder = service;
info.deathMonitor = new DeathMonitor(name, service);
try {
//注册Binder死亡通知
service.linkToDeath(info.deathMonitor, 0);
//保存本次连接信息
mActiveConnections.put(name, info);
} catch (RemoteException e) {
// This service was dead before we got it... just
// don't do anything with it.
//服务已死亡,移除连接信息
mActiveConnections.remove(name);
return;
}
} else {
// The named service is being disconnected... clean up.
mActiveConnections.remove(name);
}

//移除Binder死亡通知
if (old != null) {
old.binder.unlinkToDeath(old.deathMonitor, 0);
}
}

// If there was an old service, it is now disconnected.
//回调ServiceConnection.onServiceDisconnected
//通知client之前的连接已被断开
if (old != null) {
mConnection.onServiceDisconnected(name);
}
//如果Service死亡需要回调ServiceConnection.onBindingDied通知client服务死亡
if (dead) {
mConnection.onBindingDied(name);
}
// If there is a new viable service, it is now connected.
if (service != null) {
//回调ServiceConnection.onServiceConnected方法
//告知client已建立连接
mConnection.onServiceConnected(name, service);
} else {
// The binding machinery worked, but the remote returned null from onBind().
//当Service.onBind方法返回null,或者Service停止时
//回调ServiceConnection.onNullBinding方法
mConnection.onNullBinding(name);
}
}

这个方法其实我们在上一篇文章中已经说过了,不过在上一篇文章中我们关注的是Service绑定的部分,而这次我们关注的是解绑的部分

首先映入眼帘的就是对mForgotten变量的判断,它在客户端调用unbindService就会被设为true,然后便会直接返回,不再处理后续事项。当然,实际上执行完unbindService方法后,客户端与Service的连接会被移除,理论上应该也不会再走到这个方法里才对(这里我也感觉有点疑惑)

根据这段代码,我们能看出来Service停止后,对客户端的回调是什么:

  • Service.onBind方法的返回不为null时,此时会依次回调ServiceConnection.onServiceDisconnectedServiceConnection.onBindingDiedServiceConnection.onNullBinding方法

  • Service.onBind方法的返回为null时,此时会依次回调ServiceConnection.onBindingDiedServiceConnection.onNullBinding方法

大家也可以自己写写Demo来检验一下我说的是否正确

这一步处理完后,接下来要做的便是处理Service那边的解绑,遍历IntentBindRecord列表,调用ActivityThread$ApplicationThread.scheduleUnbindService去调度执行服务解绑操作,这里通过Handler最终调用的是ActivityThread.handleUnbindService方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//frameworks/base/core/java/android/app/ActivityThread.java
private void handleUnbindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
//回调Service.onUnbind方法,如果返回值为true
//当再次建立连接时,服务会回调Service.onRebind方法
boolean doRebind = s.onUnbind(data.intent);
try {
if (doRebind) {
ActivityManager.getService().unbindFinished(
data.token, data.intent, doRebind);
} else {
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to unbind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}

可以看到,这里回调了Service.onUnbind方法,它的返回值表示当Service后面再与其他客户端建立连接时,是否需要回调Service.onRebind方法,但是这有一个前提,那就是Service中途没有被停止,具体原因以及rebind流程我们稍后再分析

到了这一步,解绑就完成了,接下来和之前在stopSelf章节里分析的后续流程就一样了,最终调用ActivityThread.handleStopService方法停止服务,还记得我们之前分析在这个方法中触发完Service.onDestroy回调,之后还需要调用ContextImpl.scheduleFinalCleanup方法吗?现在我们就来看看这个方法又做了什么事情

1
2
3
4
//frameworks/base/core/java/android/app/ContextImpl.java
final void scheduleFinalCleanup(String who, String what) {
mMainThread.scheduleContextCleanup(this, who, what);
}

这里的mMainThread就是应用的ActivityThread

1
2
3
4
5
6
7
8
9
//frameworks/base/core/java/android/app/ActivityThread.java
final void scheduleContextCleanup(ContextImpl context, String who,
String what) {
ContextCleanupInfo cci = new ContextCleanupInfo();
cci.context = context;
cci.who = who;
cci.what = what;
sendMessage(H.CLEAN_UP_CONTEXT, cci);
}

还是老样子通过Handler发消息,最终调用的是ContextImpl.performFinalCleanup方法

1
2
3
4
//frameworks/base/core/java/android/app/ContextImpl.java
final void performFinalCleanup(String who, String what) {
mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
}

然后调用LoadedApk.removeContextRegistrations方法执行清理操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//frameworks/base/core/java/android/app/LoadedApk.java
public void removeContextRegistrations(Context context,
String who, String what) {
... //清理广播接收器

synchronized (mServices) {
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
mServices.remove(context);
if (smap != null) {
for (int i = 0; i < smap.size(); i++) {
LoadedApk.ServiceDispatcher sd = smap.valueAt(i);
... //报告ServiceConnection泄露
try {
ActivityManager.getService().unbindService(
sd.getIServiceConnection());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
sd.doForget();
}
}
mUnboundServices.remove(context);
}
}

可能看到这里,有很多小伙伴会有疑问,为什么之前已经清理过了,这里还要再进行清理、这里为什么会有ServiceConnection泄露、这里为什么还要再次unbindService?那我们需要注意了,这里被调用的对象到底是谁?其实是ServiceContext,假设我们在Serivce里调用bindService又绑定了一个其他Service,那这个Service被销毁后,它和另一个Service的连接怎么办?是不是就产生了泄露?为了防止这种情况,所以我们需要在Service销毁时调用一下这个方法解除它与其他Service的绑定

而且这个方法不仅会在这里被调用到哦,在我们之前分析过的 Android源码分析 - Activity销毁流程 中,也存在它的身影,当Activity被销毁,走到handleDestroyActivity方法时,会调用到我们ContextImpl.scheduleFinalCleanup方法,进行广播接收器的清理以及服务的解绑

至此,Service的主动停止流程我们都分析完了,还有一些细枝末节的事情可以说一说

rebind流程

之前我们说过,rebind的前提是Service中途没有被停止,为什么呢?带着疑问,我们来看之前没有分析的,当Service.onUnbind方法返回值为true时,会调用的AMS.unbindFinished方法

1
2
3
4
5
6
7
8
9
10
11
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void unbindFinished(IBinder token, Intent intent, boolean doRebind) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}

synchronized(this) {
mServices.unbindFinishedLocked((ServiceRecord)token, intent, doRebind);
}
}

还是转交给ActiveServices去实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
void unbindFinishedLocked(ServiceRecord r, Intent intent, boolean doRebind) {
final long origId = Binder.clearCallingIdentity();
try {
if (r != null) {
Intent.FilterComparison filter
= new Intent.FilterComparison(intent);
IntentBindRecord b = r.bindings.get(filter);

boolean inDestroying = mDestroyingServices.contains(r);
if (b != null) {
//服务unbind的前提就是IntentBindRecord.apps.size == 0
if (b.apps.size() > 0 && !inDestroying) {
...
} else {
// Note to tell the service the next time there is
// a new client.
//将doRebind标记置为true,下一次再次建立连接时
//服务会回调Service.onRebind方法
b.doRebind = true;
}
}

serviceDoneExecutingLocked(r, inDestroying, false);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}

根据我们之前的分析,我们知道,回调Service.onUnbind的前提就是这个Service没有任何连接与其绑定了,即IntentBindRecord.apps.size == 0,在这个case下,这个方法会将IntentBindRecorddoRebind变量置为true

此时让我们再回顾一下上一篇文章 Android源码分析 - Service启动流程 中分析的bindService流程,在ActiveServices.bindServiceLocked方法中有这么一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String instanceName, String callingPackage, final int userId)
throws TransactionTooLargeException {
...
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
//如果服务之前就已经在运行,即Service.onBind方法已经被执行,返回的IBinder对象也已经被保存
//调用LoadedApk$ServiceDispatcher$InnerConnection.connected方法
//回调ServiceConnection.onServiceConnected方法
c.conn.connected(s.name, b.intent.binder, false);

// If this is the first app connected back to this binding,
// and the service had previously asked to be told when
// rebound, then do so.
//当服务解绑,调用到Service.onUnbind方法时返回true,此时doRebind变量就会被赋值为true
//此时,当再次建立连接时,服务会回调Service.onRebind方法
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
//如果服务是因这次绑定而创建的
//请求执行Service.onBind方法,获取返回的IBinder对象
//发布Service,回调ServiceConnection.onServiceConnected方法
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
...
}

如果Service被停止了,那么它相应的ServiceRecord会从缓存mServicesByInstanceNamemServicesByIntent中移除,那么等到重新启动Service时会新建出一个ServiceRecord,此时里面的变量全部被初始化,b.intent.received == falseb.intent.requested == falseb.intent.doRebind == false,在这种情况下,调用requestServiceBindingLocked方法的最后一个入参rebindfalse,就会直接回调Service.onBind方法,而不会回调Service.onRebind方法

如果Service没有被停止,且之前有被绑定过,那么b.intent.received == true,代表IBinder对象已获取到,此时如果之前的Service.onUnbind回调返回值为true,那么这里的b.intent.doRebind也为true,再加上如果这是Service断开所有连接后建立的第一次连接,即b.intent.apps.size() == 1,那么此时调用的requestServiceBindingLocked方法的最后一个入参rebindtrue,就会直接回调Service.onRebind方法,而不会回调Service.onBind方法

混合启动的Service该如何停止

单一启动方式的Service的停止很简单,那么混合启动的Service该如何停止呢?

何为混合启动?指的是通过startService方式启动Service并且以BIND_AUTO_CREATE标志调用bindService方法绑定Service,两者不分先后

在上面的分析中,我们注意到,不管用哪种方式停止服务,最后都会走到bringDownServiceIfNeededLocked方法中,在这个方法里又会调用isServiceNeededLocked判断是否Service是否被需要,那被需要的条件是什么呢?Service.startRequestedtrue,并且没有标志为BIND_AUTO_CREATE的连接绑定,那么,只要不符合这两个条件,服务自然就可以被停止了

没有标志为BIND_AUTO_CREATE的连接绑定这个简单,只需要把标记为BIND_AUTO_CREATE的连接全部解绑了就好,那么怎么让Service.startRequestedfalse呢?我们回顾一下之前对stopSelfstopService的分析,在他们调用bringDownServiceIfNeededLocked方法之前,都会先将Service.startRequested设置为false,所以答案就很明显了:只要unbindService掉所有BIND_AUTO_CREATE的标志的连接,然后stopService就能停止掉混合启动的服务,当然你也可以先stopService,再unbindService掉所有BIND_AUTO_CREATE的标志的连接

小测验

经过以上一系列的分析,我给大家出几个小问题:

  1. 先通过startService启动服务,然后再用BIND_AUTO_CREATE标志bindService(连接1),此时,Service的生命周期是怎样的?ServiceConnection会回调哪些方法?

  2. 在上一题的基础上,再使用一个非BIND_AUTO_CREATE标志bindService(连接2),此时,Service的生命周期是怎样的?ServiceConnection会回调哪些方法?

  3. 此时,使用unbindService解绑连接1,会发生什么?ServiceConnection会回调哪些方法?

  4. 此时,使用unbindService解绑连接2,会发生什么?ServiceConnection会回调哪些方法?

  5. 接着,再使用连接1bindService,会发生什么?ServiceConnection会回调哪些方法?

  6. 然后,调用stopService停止服务,服务真的会被停止吗?Service的生命周期是怎样的?连接1的ServiceConnection会回调哪些方法?

  7. 紧接着,再使用连接2bindService,会发生什么?连接2的ServiceConnection会回调哪些方法?

  8. 最后,调用unbindService解绑连接1,会发生什么?Service的生命周期是怎样的?ServiceConnection会回调哪些方法?

请大家思考片刻,可以去回顾之前的Service启动分析以及停止流程分析,如果以上问题能全部回答正确,证明你对Service已经有了一个很深刻的理解,接下来揭晓答案:

  1. 生命周期:onCreate -> onStartCommand -> onBind,如果onBind的返回值不为nullServiceConnection会回调onServiceConnected方法,如果onBind的返回值为nullServiceConnection会回调onNullBinding方法

  2. 生命周期不会发生变化,如果onBind的返回值不为nullServiceConnection会回调onServiceConnected方法,如果onBind的返回值为nullServiceConnection会回调onNullBinding方法

  3. 生命周期不会发生变化,ServiceConnection不会回调任何方法

  4. 生命周期:onUnbindServiceConnection不会回调任何方法

  5. 如果之前onUnbind的返回值为true,则生命周期为:onRebind,否则生命周期不会发生变化,如果之前onBind的返回值不为nullServiceConnection会回调onServiceConnected方法,如果之前onBind的返回值为nullServiceConnection会回调onNullBinding方法

  6. 不会真的被停止,生命周期不会发生变化,ServiceConnection不会回调任何方法

  7. 生命周期不会发生变化,如果之前onBind的返回值不为nullServiceConnection会回调onServiceConnected方法,如果之前onBind的返回值为nullServiceConnection会回调onNullBinding方法

  8. 服务会被停止,生命周期:onUnbind -> onDestroy,连接1的ServiceConnection不会回调任何方法,如果之前onBind的返回值不为null,连接2的ServiceConnection会回调onServiceDisconnectedonBindingDied以及onNullBinding方法,如果之前onBind的返回值为null,连接2的ServiceConnection会回调onBindingDied以及onNullBinding方法

被动停止

Service除了主动停止,还会因为各种情况导致被动停止

用户手动从最近任务移除Task

注:由于各家系统对进程或任务调度策略不同,所以这里的Task移除逻辑和Service停止逻辑可能会有些许不同,我们还是以原生Android为准分析

不管用户是从最近任务划走了一个Task,还是点击了全部清除,最终都会走到ActivityStackSupervisor.removeTaskById方法

1
2
3
4
5
6
7
8
9
10
11
12
//frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
boolean removeTaskById(int taskId, boolean killProcess, boolean removeFromRecents,
String reason) {
//通过id查询Task
final Task task =
mRootWindowContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task != null) {
removeTask(task, killProcess, removeFromRecents, reason);
return true;
}
return false;
}

这里的入参,killProcesstrue,如果是移除单一Task,那么removeFromRecentstrue,如果是清除全部Task,那么removeFromRecentsfalse

先通过id,使用RootWindowContainer查询相应的Task,然后再调用removeTask方法继续移除Task

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) {
//如果Task正在清理中则直接返回
if (task.mInRemoveTask) {
// Prevent recursion.
return;
}
//标记Task正在清理中
task.mInRemoveTask = true;
try {
//销毁此Task下所有Activity
task.performClearTask(reason);
//清理Task
cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
//关闭屏幕固定
mService.getLockTaskController().clearLockedTask(task);
//通知任务栈发生变化
mService.getTaskChangeNotificationController().notifyTaskStackChanged();
//启动任务持久化程序,将任何挂起的任务写入磁盘
if (task.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
} finally {
//取消标记
task.mInRemoveTask = false;
}
}

这个方法最重要的有两个部分,一个是销毁此Task下所有Activity,对于我们本次而言不需要关注,另一个是调用cleanUpRemovedTaskLocked继续清理Task

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
void cleanUpRemovedTaskLocked(Task task, boolean killProcess, boolean removeFromRecents) {
//从最近任务列表中移除
//对于清除全部Task而言,之前在RecentTasks中已经进行过移除操作了
//所以传入的removeFromRecents为false
if (removeFromRecents) {
mRecentTasks.remove(task);
}
ComponentName component = task.getBaseIntent().getComponent();
if (component == null) {
return;
}

// Find any running services associated with this app and stop if needed.
//清理和此Task有关联的服务
final Message msg = PooledLambda.obtainMessage(ActivityManagerInternal::cleanUpServices,
mService.mAmInternal, task.mUserId, component, new Intent(task.getBaseIntent()));
mService.mH.sendMessage(msg);

//如果不需要杀死进程,到这里就为止了
if (!killProcess) {
return;
}

// Determine if the process(es) for this task should be killed.
final String pkg = component.getPackageName();
ArrayList<Object> procsToKill = new ArrayList<>();
ArrayMap<String, SparseArray<WindowProcessController>> pmap =
mService.mProcessNames.getMap();
//遍历App进程,确定要杀死的进程
for (int i = 0; i < pmap.size(); i++) {
SparseArray<WindowProcessController> uids = pmap.valueAt(i);
for (int j = 0; j < uids.size(); j++) {
WindowProcessController proc = uids.valueAt(j);
//不要杀死其他用户下的进程
if (proc.mUserId != task.mUserId) {
// Don't kill process for a different user.
continue;
}
//不要杀死首页进程
//HomeProcess指的是含有分类为android.intent.category.HOME的进程
//也就是能成为首页Launcher的进程
if (proc == mService.mHomeProcess) {
// Don't kill the home process along with tasks from the same package.
continue;
}
//不要杀死和这个Task无关的进程
if (!proc.mPkgList.contains(pkg)) {
// Don't kill process that is not associated with this task.
continue;
}

//如果这个进程有Activity在不同的Task里,并且这个Task也在最近任务里
//或者有Activity还没有被停止,则不要杀死进程
if (!proc.shouldKillProcessForRemovedTask(task)) {
// Don't kill process(es) that has an activity in a different task that is also
// in recents, or has an activity not stopped.
return;
}

//有前台服务的话不要杀死进程
if (proc.hasForegroundServices()) {
// Don't kill process(es) with foreground service.
return;
}

// Add process to kill list.
procsToKill.add(proc);
}
}

// Kill the running processes. Post on handle since we don't want to hold the service lock
// while calling into AM.
//杀死进程
final Message m = PooledLambda.obtainMessage(
ActivityManagerInternal::killProcessesForRemovedTask, mService.mAmInternal,
procsToKill);
mService.mH.sendMessage(m);
}

这个方法主要做两件事,一是清理和此Task有关联的服务,二是杀死应该杀死的进程

清理服务这一步用到了池化技术,这里大家不用管,就当做调用了mService.mAmInternal.cleanUpServices即可,这里的mService.mAmInternalAMS里的一个内部类LocalService

1
2
3
4
5
6
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void cleanUpServices(int userId, ComponentName component, Intent baseIntent) {
synchronized(ActivityManagerService.this) {
mServices.cleanUpServices(userId, component, baseIntent);
}
}

同样,关于Service的工作都转交给ActiveServices

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
void cleanUpServices(int userId, ComponentName component, Intent baseIntent) {
ArrayList<ServiceRecord> services = new ArrayList<>();
//获得此用户下所有的活动Service
ArrayMap<ComponentName, ServiceRecord> alls = getServicesLocked(userId);
//筛选出此Task下的所有活动Service
for (int i = alls.size() - 1; i >= 0; i--) {
ServiceRecord sr = alls.valueAt(i);
if (sr.packageName.equals(component.getPackageName())) {
services.add(sr);
}
}

// Take care of any running services associated with the app.
for (int i = services.size() - 1; i >= 0; i--) {
ServiceRecord sr = services.get(i);
if (sr.startRequested) {
if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {
//如果在manifest里设置了stopWithTask,那么会直接停止Service
stopServiceLocked(sr);
} else {
//如果没有设置stopWithTask的话,则会回调Service.onTaskRemoved方法
sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
sr.getLastStartId(), baseIntent, null, 0));
if (sr.app != null && sr.app.thread != null) {
// We always run in the foreground, since this is called as
// part of the "remove task" UI operation.
try {
sendServiceArgsLocked(sr, true, false);
} catch (TransactionTooLargeException e) {
// Ignore, keep going.
}
}
}
}
}
}

首先要获取到所有需要清理的Service记录,然后再对它们进行处理

对于在manifest文件里设置了stopWithTask标识的Service,直接调用stopServiceLocked方法停止服务,而对于没有这个标识的Service,则是增加一个启动项,接着调用sendServiceArgsLocked处理这个启动项

我们观察这个启动项的构建,第二个参数为true,我们可以去这个类的构造方法那里看到,第二个参数的名字为_taskRemoved,意思很明显了,然后根据我们在上一篇文章 Android源码分析 - Service启动流程 中分析的sendServiceArgsLocked方法可以知道,它最终会走到ActivityThread.handleServiceArgs方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//frameworks/base/core/java/android/app/ActivityThread.java
private void handleServiceArgs(ServiceArgsData data) {
...
int res;
if (!data.taskRemoved) {
//正常情况调用
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {
//用户关闭Task栈时调用
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}
...
}

可以发现,当taskRemoved变量为true时,会回调Service.onTaskRemoved方法

我们接着回到cleanUpRemovedTaskLocked方法中,当它清理完服务后,便会尝试杀死进程,这里面其他的判断条件我们都不用管,我们只需要关心其中的一段,有前台服务的话不要杀死进程

以上是我根据AOSP源码分析得出的结果,在模拟器上也验证通过,但在我的小米MIX4上表现却完全不是这样,大家开发时还是要以实际为准

内存不足

当内存不足时,系统会通过OOM KillerLow Memory Killer等手段杀死各种进程,如果被杀死的进程里有Service正在运行,那自然也会被停止

重建

Service所在进程被杀死后,根据Service.onStartCommand的返回值,系统会决定是否重建,怎么重建。我们先把其可能的返回值以及产生的结果先列出来:

  • START_STICKY_COMPATIBILITYtargetSdkVersion < 5 (Android 2.0) 的App默认会返回这个,Service被杀后会被重建,但onStartCommand方法不会被执行
  • START_STICKYtargetSdkVersion >= 5 (Android 2.0) 的App默认会返回这个,Service被杀后会被重建,onStartCommand方法也会被执行,但此时onStartCommand方法的第一个参数Intentnull
  • START_NOT_STICKYService被杀后不会被重建
  • START_REDELIVER_INTENTService被杀后会被重建,onStartCommand方法也会被执行,此时onStartCommand方法的第一个参数IntentService被杀死前最后一次调用onStartCommand方法时传递的Intent

对于其返回值我们需要先了解一下是怎么处理的,这需要回顾一下上一篇文章分析的handleServiceArgs方法了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//frameworks/base/core/java/android/app/ActivityThread.java
private void handleServiceArgs(ServiceArgsData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
...
int res;
if (!data.taskRemoved) {
//正常情况调用
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {
//用户关闭Task栈时调用
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}

//确保其他异步任务执行完成
QueuedWork.waitToFinish();

try {
//Service相关任务执行完成
//这一步会根据onStartCommand的返回值,调整Service死亡重建策略
//同时会把之前的启动超时定时器取消
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to start service " + s
+ " with " + data.args + ": " + e.toString(), e);
}
}
}
}

可以看到,这里从Service.onStartCommand得到返回值后以其作为参数调用了AMS.serviceDoneExecuting方法

1
2
3
4
5
6
7
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
synchronized(this) {
...
mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
}
}

转交给了ActiveServices.serviceDoneExecutingLocked方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
// This is a call from a service start... take care of
// book-keeping.
r.callStart = true;
switch (res) {
case Service.START_STICKY_COMPATIBILITY:
case Service.START_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId, false, true);
// Don't stop if killed.
r.stopIfKilled = false;
break;
}
case Service.START_NOT_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId, false, true);
if (r.getLastStartId() == startId) {
// There is no more work, and this service
// doesn't want to hang around if killed.
r.stopIfKilled = true;
}
break;
}
case Service.START_REDELIVER_INTENT: {
// We'll keep this item until they explicitly
// call stop for it, but keep track of the fact
// that it was delivered.
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false);
if (si != null) {
si.deliveryCount = 0;
si.doneExecutingCount++;
// Don't stop if killed.
r.stopIfKilled = true;
}
break;
}
case Service.START_TASK_REMOVED_COMPLETE: {
// Special processing for onTaskRemoved(). Don't
// impact normal onStartCommand() processing.
r.findDeliveredStart(startId, true, true);
break;
}
default:
throw new IllegalArgumentException(
"Unknown service start result: " + res);
}
if (res == Service.START_STICKY_COMPATIBILITY) {
r.callStart = false;
}
} else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) {
...
}
...
} else { ... }
}

我们就只看对Service.onStartCommand的返回值进行处理的部分

首先,不管返回值是什么,都会调用ServiceRecord.findDeliveredStart方法,只不过入参不同

1
2
3
4
5
6
7
8
9
10
11
12
13
//frameworks/base/services/core/java/com/android/server/am/ServiceRecord.java
public StartItem findDeliveredStart(int id, boolean taskRemoved, boolean remove) {
final int N = deliveredStarts.size();
for (int i=0; i<N; i++) {
StartItem si = deliveredStarts.get(i);
if (si.id == id && si.taskRemoved == taskRemoved) {
if (remove) deliveredStarts.remove(i);
return si;
}
}

return null;
}

前两个参数,不管返回值是什么,传进来的都是一样的,而第三个参数remove就不同了,当返回值为START_REDELIVER_INTENT的时候,它为false,其他情况都为true,意味着要删除这个已分发的启动项,START_REDELIVER_INTENT由于需要保留最后一次调用onStartCommand时的Intent,所以它不应该被删除

接着我们回到serviceDoneExecutingLocked方法,可以发现,START_STICKYSTART_STICKY_COMPATIBILITY情况下的ServiceRecord.stopIfKilled被置为了false,其他则被置为了true,这和我们之前对结果的描述不同啊?不是说好了返回START_REDELIVER_INTENT也会重启吗?这是因为START_REDELIVER_INTENT比较特殊,它的重启不需要看stopIfKilled这个标志位,这个等到我们后面分析到怎么判断是否应该停止服务时就知道了

以上的内容我们先记下来,会在后面的重启流程中发挥作用

然后我们来看进程死亡后会发生什么,我们曾在之前的文章 Android源码分析 - Activity启动流程(中) 中提到过,当App启动时,AMS会为其注册一个App进程死亡回调AppDeathRecipient,当App进程死亡后便会回调其binderDied方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;

AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread) {
mApp = app;
mPid = pid;
mAppThread = thread;
}

@Override
public void binderDied() {
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread, true, null);
}
}
}

接着便会调用appDiedLocked方法处理进程死亡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
boolean fromBinderDied, String reason) {
// First check if this ProcessRecord is actually active for the pid.
//检查pid所属ProcessRecord是否与传入ProcessRecord相符
synchronized (mPidsSelfLocked) {
ProcessRecord curProc = mPidsSelfLocked.get(pid);
if (curProc != app) {
return;
}
}

... //记录电池统计信息

//如果App进程尚未死亡的话,杀死进程
if (!app.killed) {
if (!fromBinderDied) {
killProcessQuiet(pid);
mProcessList.noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_UNKNOWN, reason);
}
ProcessList.killProcessGroup(app.uid, pid);
app.killed = true;
}

// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
//一般情况下非自动化测试,先置为true
boolean doLowMem = app.getActiveInstrumentation() == null;
boolean doOomAdj = doLowMem;
if (!app.killedByAm) { //不通过AMS杀死的进程,一般就是被 Low Memory Killer (LMK) 杀死的
... //报告信息
//被LMK杀死,说明系统内存不足
mAllowLowerMemLevel = true;
} else { //通过AMS杀死的进程
// Note that we always want to do oom adj to update our state with the
// new number of procs.
mAllowLowerMemLevel = false;
//正常情况下非内存不足
doLowMem = false;
}
... //事件记录
//继续处理App进程死亡
handleAppDiedLocked(app, false, true);

//调整进程优先级
if (doOomAdj) {
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
}
//当因为内存不足而杀死App进程时
//调用App层各处的的onLowMemory方法,释放内存
if (doLowMem) {
doLowMemReportIfNeededLocked(app);
}
} else if (app.pid != pid) { //新进程已启动
// A new process has already been started.
... //报告记录信息
}
...
}

这里的其他代码我们都不用关心,直接看handleAppDiedLocked方法继续处理App进程死亡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
...
//清理进程的主要方法
boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
false /*replacingPid*/);
...
}

final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
...
mServices.killServicesLocked(app, allowRestart);
...
}

可以看到,这里调用了ActiveServices.killServicesLocked方法停止还在运行中的服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
final void killServicesLocked(ProcessRecord app, boolean allowRestart) {
// Clean up any connections this application has to other services.
//清理所有连接
for (int i = app.connections.size() - 1; i >= 0; i--) {
ConnectionRecord r = app.connections.valueAt(i);
removeConnectionLocked(r, app, null);
}
updateServiceConnectionActivitiesLocked(app);
app.connections.clear();

app.whitelistManager = false;

// Clear app state from services.
//遍历所有正在运行中的服务
for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
ServiceRecord sr = app.getRunningServiceAt(i);
synchronized (sr.stats.getBatteryStats()) {
sr.stats.stopLaunchedLocked();
}
if (sr.app != app && sr.app != null && !sr.app.isPersistent()) {
//记录服务已停止
sr.app.stopService(sr);
//更新绑定的客户端uids
sr.app.updateBoundClientUids();
}
sr.setProcess(null);
sr.isolatedProc = null;
sr.executeNesting = 0;
sr.forceClearTracker();
if (mDestroyingServices.remove(sr)) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "killServices remove destroying " + sr);
}

final int numClients = sr.bindings.size();
for (int bindingi=numClients-1; bindingi>=0; bindingi--) {
IntentBindRecord b = sr.bindings.valueAt(bindingi);
//释放对服务Binder的引用
b.binder = null;
//重置
b.requested = b.received = b.hasBound = false;
// If this binding is coming from a cached process and is asking to keep
// the service created, then we'll kill the cached process as well -- we
// don't want to be thrashing around restarting processes that are only
// there to be cached.
... //遍历客户端进程(实际上没做任何事)
}
}

ServiceMap smap = getServiceMapLocked(app.userId);

// Now do remaining service cleanup.
for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
ServiceRecord sr = app.getRunningServiceAt(i);

// Unless the process is persistent, this process record is going away,
// so make sure the service is cleaned out of it.
//非持久化进程
if (!app.isPersistent()) {
//记录服务已停止
app.stopService(sr);
//更新绑定的客户端uids
app.updateBoundClientUids();
}

// Sanity check: if the service listed for the app is not one
// we actually are maintaining, just let it drop.
//一致性检查
final ServiceRecord curRec = smap.mServicesByInstanceName.get(sr.instanceName);
if (curRec != sr) {
if (curRec != null) {
Slog.wtf(TAG, "Service " + sr + " in process " + app
+ " not same as in map: " + curRec);
}
continue;
}

// Any services running in the application may need to be placed
// back in the pending list.
//允许重启,但Service崩溃的次数超出重试上限(默认为16),并且它不是系统应用
if (allowRestart && sr.crashCount >= mAm.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY
&& (sr.serviceInfo.applicationInfo.flags
&ApplicationInfo.FLAG_PERSISTENT) == 0) {
... //记录
//停止服务
bringDownServiceLocked(sr);
} else if (!allowRestart
|| !mAm.mUserController.isUserRunning(sr.userId, 0)) {
//不允许重启或者服务进程所在用户不在运行,停止服务
bringDownServiceLocked(sr);
} else {
//尝试调度重启服务
final boolean scheduled = scheduleServiceRestartLocked(sr, true /* allowCancel */);

// Should the service remain running? Note that in the
// extreme case of so many attempts to deliver a command
// that it failed we also will stop it here.
if (!scheduled) { //未调度重启
//停止服务
bringDownServiceLocked(sr);
} else if (sr.canStopIfKilled(false /* isStartCanceled */)) {
// Update to stopped state because the explicit start is gone. The service is
// scheduled to restart for other reason (e.g. connections) so we don't bring
// down it.
//将服务的启动状态更新为停止
sr.startRequested = false;
... //记录
}
}
}

//不允许重启的话
if (!allowRestart) {
//停止所有服务
app.stopAllServices();
//清理绑定的客户端uids
app.clearBoundClientUids();

// Make sure there are no more restarting services for this process.
//确保这个进程不再会重启服务,清理所有待重启待启动的服务
for (int i=mRestartingServices.size()-1; i>=0; i--) {
ServiceRecord r = mRestartingServices.get(i);
if (r.processName.equals(app.processName) &&
r.serviceInfo.applicationInfo.uid == app.info.uid) {
mRestartingServices.remove(i);
clearRestartingIfNeededLocked(r);
}
}
for (int i=mPendingServices.size()-1; i>=0; i--) {
ServiceRecord r = mPendingServices.get(i);
if (r.processName.equals(app.processName) &&
r.serviceInfo.applicationInfo.uid == app.info.uid) {
mPendingServices.remove(i);
}
}
}

// Make sure we have no more records on the stopping list.
//清理所有停止中的服务
int i = mDestroyingServices.size();
while (i > 0) {
i--;
ServiceRecord sr = mDestroyingServices.get(i);
if (sr.app == app) {
sr.forceClearTracker();
mDestroyingServices.remove(i);
}
}

//清理所有执行中的服务
//这里的执行中指的是有事务正在运行,比如说正在停止过程中,不是指运行中
app.executingServices.clear();
}

这里做了各种清理工作,客户端进程的清理呀,服务端进程的清理,然后就是Service重启的核心,scheduleServiceRestartLocked方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) {
//系统正在关机,直接返回
if (mAm.mAtmInternal.isShuttingDown()) {
return false;
}

//一致性检查
ServiceMap smap = getServiceMapLocked(r.userId);
if (smap.mServicesByInstanceName.get(r.instanceName) != r) {
ServiceRecord cur = smap.mServicesByInstanceName.get(r.instanceName);
Slog.wtf(TAG, "Attempting to schedule restart of " + r
+ " when found in map: " + cur);
return false;
}

final long now = SystemClock.uptimeMillis();

final String reason;
if ((r.serviceInfo.applicationInfo.flags
&ApplicationInfo.FLAG_PERSISTENT) == 0) { //对于非系统应用
//服务至少要过多长时间才能重启,默认1000ms
long minDuration = mAm.mConstants.SERVICE_RESTART_DURATION;
//服务被杀死重启后需要运行多长时间,默认60s
long resetTime = mAm.mConstants.SERVICE_RESET_RUN_DURATION;
boolean canceled = false;

// Any delivered but not yet finished starts should be put back
// on the pending list.
//对应着返回值为START_REDELIVER_INTENT的情况
final int N = r.deliveredStarts.size();
if (N > 0) {
for (int i=N-1; i>=0; i--) {
ServiceRecord.StartItem si = r.deliveredStarts.get(i);
si.removeUriPermissionsLocked();
if (si.intent == null) {
// We'll generate this again if needed.
} else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
&& si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) {
//如果该启动项的失败次数小于最大容忍次数
//MAX_DELIVERY_COUNT默认为3
//MAX_DONE_EXECUTING_COUNT默认为6
r.pendingStarts.add(0, si);
//这种情况下延时是现在距离启动时间的两倍
long dur = SystemClock.uptimeMillis() - si.deliveredTime;
dur *= 2;
if (minDuration < dur) minDuration = dur;
if (resetTime < dur) resetTime = dur;
} else {
//如果该启动项的失败次数大于等于最大容忍次数
canceled = true;
}
}
r.deliveredStarts.clear();
}

if (allowCancel) {
//判断是否应该停止(只考虑非绑定启动的情况)
final boolean shouldStop = r.canStopIfKilled(canceled);
if (shouldStop && !r.hasAutoCreateConnections()) {
// Nothing to restart.
//如果应该停止,直接返回
return false;
}
reason = (r.startRequested && !shouldStop) ? "start-requested" : "connection";
} else {
reason = "always";
}

r.totalRestartCount++;
if (r.restartDelay == 0) { //第一次重启的情况
r.restartCount++;
r.restartDelay = minDuration;
} else if (r.crashCount > 1) { //Service所在进程在Service运行过程中发生崩溃导致重启的话
//重启延时为 30min * (崩溃次数 - 1)
r.restartDelay = mAm.mConstants.BOUND_SERVICE_CRASH_RESTART_DURATION
* (r.crashCount - 1);
} else { //非第一次重启的情况
// If it has been a "reasonably long time" since the service
// was started, then reset our restart duration back to
// the beginning, so we don't infinitely increase the duration
// on a service that just occasionally gets killed (which is
// a normal case, due to process being killed to reclaim memory).
if (now > (r.restartTime+resetTime)) {
//如果服务重启后运行达到了一定时间,则重启延时为
r.restartCount = 1;
r.restartDelay = minDuration;
} else {
//如果服务重启后运行没有达到一定时间(短时间内又要重启)
//则增长重启延时,默认因子为4
r.restartDelay *= mAm.mConstants.SERVICE_RESTART_DURATION_FACTOR;
if (r.restartDelay < minDuration) {
r.restartDelay = minDuration;
}
}
}

//确定重启时间
r.nextRestartTime = now + r.restartDelay;

// Make sure that we don't end up restarting a bunch of services
// all at the same time.
//确保不会在同一时间启动大量服务
boolean repeat;
do {
repeat = false;
final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN;
for (int i=mRestartingServices.size()-1; i>=0; i--) {
ServiceRecord r2 = mRestartingServices.get(i);
if (r2 != r && r.nextRestartTime >= (r2.nextRestartTime-restartTimeBetween)
&& r.nextRestartTime < (r2.nextRestartTime+restartTimeBetween)) {
r.nextRestartTime = r2.nextRestartTime + restartTimeBetween;
r.restartDelay = r.nextRestartTime - now;
repeat = true;
break;
}
}
} while (repeat);

} else { //对于系统进程,立马重启
// Persistent processes are immediately restarted, so there is no
// reason to hold of on restarting their services.
r.totalRestartCount++;
r.restartCount = 0;
r.restartDelay = 0;
r.nextRestartTime = now;
reason = "persistent";
}

//添加到重启列表中
if (!mRestartingServices.contains(r)) {
r.createdFromFg = false;
mRestartingServices.add(r);
r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
}

//取消前台服务通知
cancelForegroundNotificationLocked(r);

//通过Handler调度重启
mAm.mHandler.removeCallbacks(r.restarter);
mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
... //事件记录
return true;
}

这个方法主要做了几件事情,一是判断服务需不需要重启,二是计算服务的下次重启时间,最后通过Handler执行延时重启

还记得我们前面说的当返回值为START_REDELIVER_INTENT时,不会从ServiceRecord.deliveredStarts中删除启动项吗?这里就体现出了这一点,遍历整个deliveredStarts列表,从中找出符合重启条件的启动项,将其加入到pendingStarts列表中,需要注意的是,在这种情况下,重启延时为现在距离启动时间的两倍,所以一般情况下START_REDELIVER_INTENTSTART_STICKY重启的要更慢

接下来便要判断服务需不需要重启,这里调用了ServiceRecord.canStopIfKilled方法

1
2
3
4
//frameworks/base/services/core/java/com/android/server/am/ServiceRecord.java
boolean canStopIfKilled(boolean isStartCanceled) {
return startRequested && (stopIfKilled || isStartCanceled) && pendingStarts.isEmpty();
}

之前说过,如果返回值为START_STICKYSTART_STICKY_COMPATIBILITY,那这里的stopIfKilled就为false,所以整体会返回false,而对于返回值START_REDELIVER_INTENT而言,之前已经进行过操作,将启动项添加到pendingStarts列表中了,所以只要这里为false,整体就为falsestopIfKilled的值就不重要了

最后就是通过Handler执行延时重启了,这里Handler传入的RunnableServiceRecord.restarter,它是在服务启动,调用retrieveServiceLocked方法时被创建的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
private class ServiceRestarter implements Runnable {
private ServiceRecord mService;

void setService(ServiceRecord service) {
mService = service;
}

public void run() {
synchronized(mAm) {
performServiceRestartLocked(mService);
}
}
}

可以看到,在一定的延时后,会调用到performServiceRestartLocked方法重启服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
final void performServiceRestartLocked(ServiceRecord r) {
if (!mRestartingServices.contains(r)) {
return;
}
if (!isServiceNeededLocked(r, false, false)) {
// Paranoia: is this service actually needed? In theory a service that is not
// needed should never remain on the restart list. In practice... well, there
// have been bugs where this happens, and bad things happen because the process
// ends up just being cached, so quickly killed, then restarted again and again.
// Let's not let that happen.
Slog.wtf(TAG, "Restarting service that is not needed: " + r);
return;
}
try {
//参考上一篇文章,Service启动流程
bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false);
} catch (TransactionTooLargeException e) {
// Ignore, it's been logged and nothing upstack cares.
}
}

最终调用了bringUpServiceLocked方法启动服务,这一部分可以看上一篇文章 Android源码分析 - Service启动流程 中的分析,还记得上一篇文章中分析的realStartServiceLocked方法中,有一个逻辑是:如果Service已经启动,并且没有启动项,则构建一个假的启动参数供onStartCommand使用 吗?之前看到这个逻辑的时候我还有些疑惑为什么需要这样,现在就豁然开朗了,原来这是为Service重启做的逻辑,而对于返回值START_REDELIVER_INTENT而言,pendingStarts列表本身就不为空,直接正常执行启动任务就可以了

总结

至此,整个Service篇章就到此结束了,通过这次的写作,我自身也是受益匪浅,了解到了很多我以前不知道的知识,也纠正了一些我以前错误的认知,如果也能帮到正在看文章的你们,那就再好不过了


Android源码分析 - Service启动流程

开篇

本篇以android-11.0.0_r25作为基础解析

在之前的文章中,我们已经分析过了四大组件中ActivityContentProvider的启动流程,这次我们就来讲讲四大组件之一的Service是如何启动和绑定的

流程图

在查阅资料的过程中,我发现有些博主会将梳理好的流程图贴在开头,我觉得这样有助于从宏观上去理解源码的整个流程和设计理念,所以以后的文章我都会尽量将源码梳理成流程图,以便大家理解

startService流程图

bindService流程图

入口

启动Service有两种方式,一是startService,一是bindService,它们最终的实现都在ContextImpl

Context.startService

Service通过这种方式启动后,会一直运行下去,直到外部调用了stopService或内部调用stopSelf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//frameworks/base/core/java/android/app/ContextImpl.java
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}

private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
//确保Intent有效
validateServiceIntent(service);
//跨进程准备
service.prepareToLeaveProcess(this);
//调用AMS.startService
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
getOpPackageName(), getAttributionTag(), user.getIdentifier());
//通过AMS层返回的ComponentName.packageName来判断是否出错以及错误类型
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
throw new SecurityException(
"Unable to start service " + service
+ ": " + cn.getClassName());
} else if (cn.getPackageName().equals("?")) {
throw new IllegalStateException(
"Not allowed to start service " + service + ": " + cn.getClassName());
}
}
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}

从代码可以看出,这里就是做了一下简单的校验,然后便调用了AMS.startService启动Service,最终通过返回的ComponentName中的packageName来判断是否出错以及错误类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, boolean requireForeground, String callingPackage,
String callingFeatureId, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
//校验Intent,不允许其携带fd
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}

//调用方包名不能为空
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}

synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res;
try {
//调用ActiveServices.startServiceLocked方法
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
requireForeground, callingPackage, callingFeatureId, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
return res;
}
}

同样,这里做了一些简单的检查,然后调用ActiveServices.startServiceLocked方法,ActiveServices是一个辅助AMS进行Service管理的类,包括Service的启动、绑定和停止等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage,
@Nullable String callingFeatureId, final int userId)
throws TransactionTooLargeException {
return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
callingPackage, callingFeatureId, userId, false);
}

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage,
@Nullable String callingFeatureId, final int userId,
boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
//判断调用方是否为前台
final boolean callerFg;
if (caller != null) {
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + callingPid
+ ") when starting service " + service);
}
callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
} else {
callerFg = true;
}

//查找待启动Service
ServiceLookupResult res =
retrieveServiceLocked(service, null, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false, false);
//如果找不到待启动Service,直接返回null
if (res == null) {
return null;
}
//如果待启动的Service所在package和uid无法与调用方package和uid建立关联,则无法启动Service
//返回异常ComponentName,由上层抛出SecurityException异常
if (res.record == null) {
return new ComponentName("!", res.permission != null
? res.permission : "private to package");
}

ServiceRecord r = res.record;

//试图用一个不存在的用户启动Service
if (!mAm.mUserController.exists(r.userId)) {
Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
return null;
}

// If we're starting indirectly (e.g. from PendingIntent), figure out whether
// we're launching into an app in a background state. This keys off of the same
// idleness state tracking as e.g. O+ background service start policy.
//Service所在应用未启动或处在后台
final boolean bgLaunch = !mAm.isUidActiveLocked(r.appInfo.uid);

// If the app has strict background restrictions, we treat any bg service
// start analogously to the legacy-app forced-restrictions case, regardless
// of its target SDK version.
//检查Service所在应用后台启动限制
boolean forcedStandby = false;
if (bgLaunch && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
forcedStandby = true;
}

// If this is a direct-to-foreground start, make sure it is allowed as per the app op.
boolean forceSilentAbort = false;
if (fgRequired) { //作为前台服务启动
//权限检查
final int mode = mAm.getAppOpsManager().checkOpNoThrow(
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
switch (mode) {
//默认和允许都可以作为前台服务启动
case AppOpsManager.MODE_ALLOWED:
case AppOpsManager.MODE_DEFAULT:
// All okay.
break;
//不允许的话,回退到作为普通后台服务启动
case AppOpsManager.MODE_IGNORED:
// Not allowed, fall back to normal start service, failing siliently
// if background check restricts that.
Slog.w(TAG, "startForegroundService not allowed due to app op: service "
+ service + " to " + r.shortInstanceName
+ " from pid=" + callingPid + " uid=" + callingUid
+ " pkg=" + callingPackage);
fgRequired = false;
forceSilentAbort = true;
break;
//错误的话直接返回,由上层抛出SecurityException异常
default:
return new ComponentName("!!", "foreground not allowed as per app op");
}
}

// If this isn't a direct-to-foreground start, check our ability to kick off an
// arbitrary service
//如果不是从前台启动
//startRequested表示Service是否由startService方式所启动,fgRequired表示作为前台服务启动
if (forcedStandby || (!r.startRequested && !fgRequired)) {
// Before going further -- if this app is not allowed to start services in the
// background, then at this point we aren't going to let it period.
//服务是否允许在后台启动
final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
//如果不允许,则无法启动服务
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
//静默的停止启动
if (allowed == ActivityManager.APP_START_MODE_DELAYED || forceSilentAbort) {
// In this case we are silently disabling the app, to disrupt as
// little as possible existing apps.
return null;
}
if (forcedStandby) {
// This is an O+ app, but we might be here because the user has placed
// it under strict background restrictions. Don't punish the app if it's
// trying to do the right thing but we're denying it for that reason.
if (fgRequired) {
return null;
}
}
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
//明确的告知不允许启动,上层抛出异常
UidRecord uidRec = mAm.mProcessList.getUidRecordLocked(r.appInfo.uid);
return new ComponentName("?", "app is in background uid " + uidRec);
}
}

// At this point we've applied allowed-to-start policy based on whether this was
// an ordinary startService() or a startForegroundService(). Now, only require that
// the app follow through on the startForegroundService() -> startForeground()
// contract if it actually targets O+.
//对于targetSdk 26以下(Android 8.0以下)的应用来说,不需要作为前台服务启动
if (r.appInfo.targetSdkVersion < Build.VERSION_CODES.O && fgRequired) {
fgRequired = false;
}

//检查通过Intent被临时授权的Uris
NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent(
service, callingUid, r.packageName, r.userId);

// If permissions need a review before any of the app components can run,
// we do not start the service and launch a review activity if the calling app
// is in the foreground passing it a pending intent to start the service when
// review is completed.

// XXX This is not dealing with fgRequired!
//如果待启动的Service需要相应权限,则需要用户手动确认权限后,再进行启动
if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage, callingFeatureId,
callingUid, service, callerFg, userId)) {
return null;
}

//取消之前的Service重启任务(如果有)
if (unscheduleServiceRestartLocked(r, callingUid, false)) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);
}
r.lastActivity = SystemClock.uptimeMillis();
//表示Service是否由startService方式所启动的
r.startRequested = true;
r.delayedStop = false;
//是否作为前台服务启动
r.fgRequired = fgRequired;
//构造启动参数
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants, callingUid));

//作为前台服务启动
if (fgRequired) {
// We are now effectively running a foreground service.
... //使用ServiceState记录
... //通过AppOpsService监控
}

final ServiceMap smap = getServiceMapLocked(r.userId);
boolean addToStarting = false;
//对于后台启动的非前台服务,需要判断其是否需要延迟启动
if (!callerFg && !fgRequired && r.app == null
&& mAm.mUserController.hasStartedUserState(r.userId)) {
//获取Service所处进程信息
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
//没有对应进程或进程状态级别低于 [进程在后台运行Receiver]
if (proc == null || proc.getCurProcState() > ActivityManager.PROCESS_STATE_RECEIVER) {
// If this is not coming from a foreground caller, then we may want
// to delay the start if there are already other background services
// that are starting. This is to avoid process start spam when lots
// of applications are all handling things like connectivity broadcasts.
// We only do this for cached processes, because otherwise an application
// can have assumptions about calling startService() for a service to run
// in its own process, and for that process to not be killed before the
// service is started. This is especially the case for receivers, which
// may start a service in onReceive() to do some additional work and have
// initialized some global state as part of that.
//对于之前已经设置为延迟启动的服务,直接返回
if (r.delayed) {
// This service is already scheduled for a delayed start; just leave
// it still waiting.
return r.name;
}
//如果当前正在后台启动的Service数大于等于允许同时在后台启动的最大服务数
//将这个Service设置为延迟启动
if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
// Something else is starting, delay!
smap.mDelayedStartList.add(r);
r.delayed = true;
return r.name;
}
//添加到正在启动服务列表中
addToStarting = true;
} else if (proc.getCurProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
//进程状态为 [正在运行Service的后台进程] 或 [正在运行Receiver的后台进程] 时
// We slightly loosen when we will enqueue this new service as a background
// starting service we are waiting for, to also include processes that are
// currently running other services or receivers.
//添加到正在启动服务列表中
addToStarting = true;
}
}

//如果允许Service后台启动Activity,则将其加入到白名单中
if (allowBackgroundActivityStarts) {
r.whitelistBgActivityStartsOnServiceStart();
}
//继续启动Service
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);

//检查是否允许前台服务使用while-in-use权限
if (!r.mAllowWhileInUsePermissionInFgs) {
r.mAllowWhileInUsePermissionInFgs =
shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid,
callingUid, service, r, allowBackgroundActivityStarts);
}

return cmp;
}

这个方法涉及到很多前后台判断,我想这里的前后台其实分为三个概念,一是调用方App是否在前台,二是Service方App是否在前台,三是Service是否作为前台服务启动,当然,大部分情况启动的都是App内的Service,即一二中的前后台状态是一致的,但也不排除启动其他App的Service这种情况,所以这里还是需要好好区分开来

这个方法看起来很长,但总之都是一些Service启动前的预处理工作,主要做了以下几点工作:

  1. 判断调用方进程是否在前台(callerFg):对于调用方在后台启动的Service,需要判断其是否需要延迟启动
  2. 调用retrieveServiceLocked查找待启动Service信息(ServiceRecord
  3. 各种检查,一旦发现不满足启动条件就终止启动Service
  4. 检查Service所在应用的前后台状态以及后台启动限制,不符合条件则终止启动Service
  5. 判断是否可以作为前台服务启动
  6. 如果待启动的Service需要相应权限,则需要用户手动确认权限后,再进行启动
  7. 取消之前的Service重启任务(如果有)
  8. 设置ServiceRecord状态,包括上次活动时间,是否由startService方式所启动的,是否作为前台服务启动等
  9. 如果作为前台服务启动,则需要进行记录和监控
  10. 对于后台启动的非前台服务,需要判断其是否需要延迟启动
  11. 调用startServiceInnerLocked继续启动Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
... //记录
//启动前初始化
r.callStart = false;
... //记录
//拉起服务,如果服务未启动,则会启动服务并调用其onCreate和onStartCommand方法
//如果服务已启动,由于之前构造了启动参数,则会直接调用其onStartCommand方法
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
if (error != null) {
return new ComponentName("!!", error);
}

if (r.startRequested && addToStarting) { //对于后台启动服务的情况
//是否为第一个后台启动的服务
boolean first = smap.mStartingBackground.size() == 0;
//添加到正在后台启动服务列表中
smap.mStartingBackground.add(r);
//设置后台启动服务超时时间(默认15秒)
r.startingBgTimeout = SystemClock.uptimeMillis() + mAm.mConstants.BG_START_TIMEOUT;
//如果为第一个后台启动的服务,则代表后面暂时没有正在后台启动的服务了
//此时将之前设置为延迟启动的服务调度出来后台启动
if (first) {
smap.rescheduleDelayedStartsLocked();
}
} else if (callerFg || r.fgRequired) { //对于调用方进程为前台或作为前台服务启动的情况
//将此Service从正在后台启动服务列表和延迟启动服务列表中移除
//如果正在后台启动服务列表中存在此服务的话,将之前设置为延迟启动的服务调度出来后台启动
smap.ensureNotStartingBackgroundLocked(r);
}

return r.name;
}

在这个方法中,首先会调用bringUpServiceLocked方法拉起服务,然后根据服务是否为前台启动,分别调用ServiceMap.rescheduleDelayedStartsLockedServiceMap.ensureNotStartingBackgroundLocked方法从后台延迟启动服务列表mDelayedStartList中不断地调度启动服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
//如果Service所在的进程存在,并且其IApplicationThread也存在
//说明服务已启动(因为在启动服务时,会给ServiceRecord.app赋值,并且app.thread不为null说明进程没有被杀死)
//此时直接拉起Service.onStartCommand方法
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}

//如果服务正在重启中,则什么都不做,直接返回
if (!whileRestarting && mRestartingServices.contains(r)) {
// If waiting for a restart, then do nothing.
return null;
}

// We are now bringing the service up, so no longer in the
// restarting state.
//Service马上启动,将其从重启中服务列表中移除,并清除其重启中状态
if (mRestartingServices.remove(r)) {
clearRestartingIfNeededLocked(r);
}

// Make sure this service is no longer considered delayed, we are starting it now.
//走到这里,需要确保此服务不再被视为延迟启动,同时将其从延迟启动服务列表中移除
if (r.delayed) {
getServiceMapLocked(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
}

// Make sure that the user who owns this service is started. If not,
// we don't want to allow it to run.
//确保Service所在的用户已启动
if (!mAm.mUserController.hasStartedUserState(r.userId)) {
//停止服务
bringDownServiceLocked(r);
return msg;
}

// Service is now being launched, its package can't be stopped.
//Service即将启动,Service所属的App不该为stopped状态
//将App状态置为unstopped,设置休眠状态为false
AppGlobals.getPackageManager().setPackageStoppedState(
r.packageName, false, r.userId);

//服务所在进程是否为隔离进程,指服务是否在其自己的独立进程中运行
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processName;
HostingRecord hostingRecord = new HostingRecord("service", r.instanceName);
ProcessRecord app;

if (!isolated) { //非隔离进程
//获取进程的ProcessRecord对象
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (app != null && app.thread != null) {
//将App添加至进程中运行的包列表中
app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
//接着启动Service
realStartServiceLocked(r, app, execInFg);
return null;

// If a dead object exception was thrown -- fall through to
// restart the application.
}
} else { //隔离进程
// If this service runs in an isolated process, then each time
// we call startProcessLocked() we will get a new isolated
// process, starting another process if we are currently waiting
// for a previous process to come up. To deal with this, we store
// in the service any current isolated process it is running in or
// waiting to have come up.
//获取服务之前所在的进程
app = r.isolatedProc;
//辅助zygote进程,用于创建isolated_app进程来渲染不可信的web内容,具有最为严格的安全限制
if (WebViewZygote.isMultiprocessEnabled()
&& r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
hostingRecord = HostingRecord.byWebviewZygote(r.instanceName);
}
//应用zygote进程,与常规zygote创建的应用相比受到更多限制
if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) {
hostingRecord = HostingRecord.byAppZygote(r.instanceName, r.definingPackageName,
r.definingUid);
}
}

// Not running -- get it started, and enqueue this service record
// to be executed when the app comes up.
//如果Service所在进程尚未启动
if (app == null && !permissionsReviewRequired) {
// TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service
// was initiated from a notification tap or not.
//启动App进程
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
//如果启动进程失败,停止服务
bringDownServiceLocked(r);
return msg;
}
if (isolated) {
//如果是隔离进程,将这次启动的进程记录保存下来
r.isolatedProc = app;
}
}

//对于要启动的前台服务,加入到临时白名单,暂时绕过省电模式
if (r.fgRequired) {
mAm.tempWhitelistUidLocked(r.appInfo.uid,
SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch");
}

//将启动的服务添加到mPendingServices列表中
//如果服务进程尚未启动,进程在启动的过程中会检查此列表并启动需要启动的Service
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}

//Service被要求stop,停止服务
if (r.delayedStop) {
// Oh and hey we've already been asked to stop!
r.delayedStop = false;
if (r.startRequested) {
stopServiceLocked(r);
}
}

return null;
}

这个方法看起来长,其实做的事情并不多:

  1. 如果Service已经启动,则调用sendServiceArgsLocked方法直接拉起Service.onStartCommand方法
  2. 各种检查准备操作(待重启、用户是否启动等,具体见注释)
  3. 如果Service所在进程已启动,调用realStartServiceLocked方法接着启动Service
  4. 如果Service所在进程未启动,调用AMS.startProcessLocked方法启动进程
  5. 将要启动的Service添加到mPendingServices列表中,对于Service所在进程未启动的这种情况,在进程的启动过程中会检查此列表并启动需要启动的Service(即此Service

从这里可以看出来,Service的启动分为两个分支,一个是进程已启动,一个是进程未启动

进程未启动

在进程未启动的情况下,这里会调用AMS.startProcessLocked方法启动进程,接着等待进程启动完成后,会调用到AMS.attachApplicationLocked方法,在这个方法中有一段关于Service启动的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
...
// Find any services that should be running in this process...
//检查是否有Services等待启动
if (!badApp) {
try {
didSomething |= mServices.attachApplicationLocked(app, processName);
} catch (Exception e) {
badApp = true;
}
}
...
}

可以看到,在这里调用了ActiveServices.attachApplicationLocked方法去启动待启动的Service

关于App进程启动的流程详见我之前的文章 Android源码分析 - Activity启动流程(中) ,这里就不赘述了

ActiveServices.attachApplicationLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
boolean attachApplicationLocked(ProcessRecord proc, String processName)
throws RemoteException {
boolean didSomething = false;

// Update the app background restriction of the caller
//更新Service所在App后台限制
proc.mState.setBackgroundRestricted(appRestrictedAnyInBackground(
proc.uid, proc.info.packageName));

// Collect any services that are waiting for this process to come up.
//启动mPendingServices列表内,该进程下的所有Service
if (mPendingServices.size() > 0) {
ServiceRecord sr = null;
try {
for (int i=0; i<mPendingServices.size(); i++) {
sr = mPendingServices.get(i);
if (proc != sr.isolationHostProc && (proc.uid != sr.appInfo.uid
|| !processName.equals(sr.processName))) {
continue;
}

final IApplicationThread thread = proc.getThread();
final int pid = proc.getPid();
final UidRecord uidRecord = proc.getUidRecord();
mPendingServices.remove(i);
i--;
//将App添加至进程中运行的包列表中
proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode,
mAm.mProcessStats);
//启动Service
realStartServiceLocked(sr, proc, thread, pid, uidRecord, sr.createdFromFg,
true);
didSomething = true;
//如果此Service不再需要了,则停止它
//e.g. 通过bindService启动的服务,但此时调用bindService的Activity已死亡
if (!isServiceNeededLocked(sr, false, false)) {
// We were waiting for this service to start, but it is actually no
// longer needed. This could happen because bringDownServiceIfNeeded
// won't bring down a service that is pending... so now the pending
// is done, so let's drop it.
bringDownServiceLocked(sr, true);
}
/* Will be a no-op if nothing pending */
//更新进程优先级
mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
}
} catch (RemoteException e) {
throw e;
}
}
// Also, if there are any services that are waiting to restart and
// would run in this process, now is a good time to start them. It would
// be weird to bring up the process but arbitrarily not let the services
// run at this point just because their restart time hasn't come up.
//App被杀重启机制,后续文章再详细说明
if (mRestartingServices.size() > 0) {
...
}
return didSomething;
}

这个方法会从mPendingServices列表内寻找该进程下的所有待启动Service,然后调用ActiveServices.realStartServiceLocked方法启动它

进程已启动

对于进程已启动的情况,我们通过ActiveServices.bringUpServiceLocked方法也可以得知,调用了ActiveServices.realStartServiceLocked方法,所以不管进程是否启动,最终都会殊途同归走到ActiveServices.realStartServiceLocked方法启动Service

ActiveServices.realStartServiceLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
/**
* Note the name of this method should not be confused with the started services concept.
* The "start" here means bring up the instance in the client, and this method is called
* from bindService() as well.
*/
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
//IApplicationThread不存在则抛移除
//即确保ActivityThread存在
if (app.thread == null) {
throw new RemoteException();
}
//为ServiceRecord设置所属进程
r.setProcess(app);
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();

//在此进程中将Service记录为运行中
//返回值为此Service是否之前未启动
final boolean newService = app.startService(r);
//记录Service执行操作并设置超时回调
//前台服务超时时间为20s,后台服务超时时间为200s
bumpServiceExecutingLocked(r, execInFg, "create");
//更新进程优先级
mAm.updateLruProcessLocked(app, false, null);
updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
mAm.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_START_SERVICE);

boolean created = false;
try {
... //记录
//记录信息
mAm.notifyPackageUse(r.serviceInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
//设置进程状态
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
//回到App进程,调度创建Service
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.getReportedProcState());
//显示前台服务通知
r.postNotification();
created = true;
} catch (DeadObjectException e) {
//杀死进程
mAm.appDiedLocked(app, "Died when creating service");
throw e;
} finally {
//如果没能成功创建Service
if (!created) {
// Keep the executeNesting count accurate.
//保证executeNesting计数的准确
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying);

// Cleanup.
//停止服务,清除信息
if (newService) {
app.stopService(r);
r.setProcess(null);
}

// Retry.
//重试
if (!inDestroying) {
scheduleServiceRestartLocked(r, false);
}
}
}

//允许管理白名单,如省电模式白名单
if (r.whitelistManager) {
app.whitelistManager = true;
}

//执行Service.onBind方法(通过bindService启动的情况下)
requestServiceBindingsLocked(r, execInFg);

//更新是否有与Service建立连接的Activity
updateServiceClientActivitiesLocked(app, null, true);

//添加绑定到Service所在进程的UID
if (newService && created) {
app.addBoundClientUidsOfNewService(r);
}

// If the service is in the started state, and there are no
// pending arguments, then fake up one so its onStartCommand() will
// be called.
//如果Service已经启动,并且没有启动项,则构建一个假的启动参数供onStartCommand使用
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
null, null, 0));
}

//拉起Service.onStartCommand方法
sendServiceArgsLocked(r, execInFg, true);

//走到这里,需要确保此服务不再被视为延迟启动,同时将其从延迟启动服务列表中移除
if (r.delayed) {
getServiceMapLocked(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
}

//Service被要求stop,停止服务
if (r.delayedStop) {
// Oh and hey we've already been asked to stop!
r.delayedStop = false;
if (r.startRequested) {
stopServiceLocked(r);
}
}
}

创建Service

到了这一步,进程理应启动和初始化完成了,接下来就该实际的去创建Service并启动它了,首先创建Service这一块我们看app.thread.scheduleCreateService方法,这里的appProcessRecord,里面的threadIApplicationThreadActivityThread中的内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
//frameworks/base/core/java/android/app/ActivityThread.java
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
//更新进程状态
updateProcessState(processState, false);
//将创建Service的必要信息包装
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
//通过Handler发送Message
sendMessage(H.CREATE_SERVICE, s);
}

这里将创建Service的必要信息包装成CreateServiceData对象后,通过Handler发送Message处理服务创建

1
2
3
4
5
6
7
8
9
10
11
//frameworks/base/core/java/android/app/ActivityThread.java
public void handleMessage(Message msg) {
switch (msg.what) {
...
case CREATE_SERVICE:
handleCreateService((CreateServiceData)msg.obj);
break;
...
}
...
}

ActivityThreadHandler在接收到CREATE_SERVICE消息后调用了handleCreateService方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//frameworks/base/core/java/android/app/ActivityThread.java
private void handleCreateService(CreateServiceData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
//此时不要进行GC
unscheduleGcIdler();

LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
//创建Context
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
//创建或获取Application(到了这里进程的初始化应该都完成了,所以是直接获取Application)
Application app = packageInfo.makeApplication(false, mInstrumentation);
java.lang.ClassLoader cl = packageInfo.getClassLoader();
//通过AppComponentFactory反射创建Service实例
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
// Service resources must be initialized with the same loaders as the application
// context.
//加载资源
context.getResources().addLoaders(
app.getResources().getLoaders().toArray(new ResourcesLoader[0]));

context.setOuterContext(service);
//初始化
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
//执行onCreate回调
service.onCreate();
//保存运行中的Service
mServices.put(data.token, service);
try {
//Service相关任务执行完成
//这一步中会把之前的启动超时定时器取消
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}

可以看到,Service的创建和之前文章中所分析的Activity的创建流程基本一致,都是创建Context,通过AppComponentFactory反射实例化对象,然后加载资源,attach做绑定,最后执行onCreate回调

1
2
3
4
5
6
//frameworks/base/core/java/android/app/AppComponentFactory.java
public @NonNull Service instantiateService(@NonNull ClassLoader cl,
@NonNull String className, @Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Service) cl.loadClass(className).newInstance();
}

如果没有特别在AndroidManifest.xml中设置android:appComponentFactory的话,默认的实现就是这样,通过传进来的ClassLoaderclassName反射实例化Service对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//frameworks/base/core/java/android/app/Service.java
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
//绑定BaseContext
attachBaseContext(context);
mThread = thread; // NOTE: unused - remove?
mClassName = className;
//保存ServiceRecord
mToken = token;
mApplication = application;
mActivityManager = (IActivityManager)activityManager;
//启动兼容性设置
mStartCompatibility = getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.ECLAIR;
//设置内容捕获功能
setContentCaptureOptions(application.getContentCaptureOptions());
}

attach方法也很简单,做了一些绑定Context等基本操作

最后调用onCreate方法,这个方法默认是个空实现,让继承Service的类去实现这个方法

启动Service

Service创建完成后就该启动它了,这里对应着ActiveServices.realStartServiceLocked方法中调用的sendServiceArgsLocked方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
boolean oomAdjusted) throws TransactionTooLargeException {
//如果待启动项列表中没有内容则直接返回
final int N = r.pendingStarts.size();
if (N == 0) {
return;
}

ArrayList<ServiceStartArgs> args = new ArrayList<>();

//遍历待启动项
while (r.pendingStarts.size() > 0) {
ServiceRecord.StartItem si = r.pendingStarts.remove(0);
//如果在多个启动项中有假启动项,则跳过假启动项
//但如果这个假启动项是唯一的启动项则不要跳过它,这是为了支持onStartCommand(null)的情况
if (si.intent == null && N > 1) {
// If somehow we got a dummy null intent in the middle,
// then skip it. DO NOT skip a null intent when it is
// the only one in the list -- this is to support the
// onStartCommand(null) case.
continue;
}
si.deliveredTime = SystemClock.uptimeMillis();
r.deliveredStarts.add(si);
si.deliveryCount++;
//处理Uri权限
if (si.neededGrants != null) {
mAm.mUgmInternal.grantUriPermissionUncheckedFromIntent(si.neededGrants,
si.getUriPermissionsLocked());
}
//授权访问权限
mAm.grantImplicitAccess(r.userId, si.intent, si.callingId,
UserHandle.getAppId(r.appInfo.uid)
);
//记录Service执行操作并设置超时回调
//前台服务超时时间为20s,后台服务超时时间为200s
bumpServiceExecutingLocked(r, execInFg, "start");
if (!oomAdjusted) {
oomAdjusted = true;
mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
}
//如果是以前台服务的方式启动的Service(startForegroundService),并且之前没有设置启动前台服务超时回调
if (r.fgRequired && !r.fgWaiting) {
//如果当前服务还没成为前台服务,设置启动前台服务超时回调
//在10s内需要调用Service.startForeground成为前台服务,否则停止服务
//注:Android 11这个超时时间是10s,在后面的Android版本中这个时间有变化
if (!r.isForeground) {
scheduleServiceForegroundTransitionTimeoutLocked(r);
} else {
r.fgRequired = false;
}
}
int flags = 0;
if (si.deliveryCount > 1) {
flags |= Service.START_FLAG_RETRY;
}
if (si.doneExecutingCount > 0) {
flags |= Service.START_FLAG_REDELIVERY;
}
//添加启动项
args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent));
}

//构建出一个支持Binder跨进程传输大量数据的列表来传输启动参数数据
ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
slice.setInlineCountLimit(4);
try {
//回到App进程,调度启动Service
r.app.thread.scheduleServiceArgs(r, slice);
} catch ...
}

这个方法中有几个比较重要的点需要注意:

  1. pendingStarts中至少要有一个启动项才会执行onStartCommand,所以在前面的ActiveServices.realStartServiceLocked方法中才会有这样一段代码:如果Service已经启动,并且没有启动项,则构建一个假的启动参数供onStartCommand使用
  2. 如果在多个启动项中有假启动项,则跳过假启动项,但如果这个假启动项是唯一的启动项则不要跳过它,这是为了支持onStartCommand方法的第一个参数Intentnull的情况
  3. 如果服务是以前台服务的方式启动的(startForegroundService),如果当前服务还没成为前台服务,则需要设置一个启动前台服务的超时回调,如果在限制的时间范围内还没有成为前台服务(调用Service.startForeground方法),则会触发超时逻辑,停止服务,这个时间在Android 11上是10s,在后面的Android版本中有变化
  4. 最后将所有的启动项放到一个支持Binder跨进程传输大量数据的列表中,然后调用App进程中的ActivityThread$ApplicationThread.scheduleServiceArgs方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//frameworks/base/core/java/android/app/ActivityThread.java
public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {
List<ServiceStartArgs> list = args.getList();

for (int i = 0; i < list.size(); i++) {
ServiceStartArgs ssa = list.get(i);
ServiceArgsData s = new ServiceArgsData();
s.token = token;
s.taskRemoved = ssa.taskRemoved;
s.startId = ssa.startId;
s.flags = ssa.flags;
s.args = ssa.args;

sendMessage(H.SERVICE_ARGS, s);
}
}

和前面一样,这里也是将启动Service的必要信息包装成一个个ServiceStartArgs对象后,通过Handler依次发送Message处理服务启动,这里最终调用的是ActivityThread.handleServiceArgs方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//frameworks/base/core/java/android/app/ActivityThread.java
private void handleServiceArgs(ServiceArgsData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
//Intent跨进程处理
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
data.args.prepareToEnterProcess();
}
int res;
if (!data.taskRemoved) {
//正常情况调用
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {
//用户关闭Task栈时调用
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}

//确保其他异步任务执行完成
QueuedWork.waitToFinish();

try {
//Service相关任务执行完成
//这一步会根据onStartCommand的返回值,调整Service死亡重建策略
//同时会把之前的启动超时定时器取消
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to start service " + s
+ " with " + data.args + ": " + e.toString(), e);
}
}
}
}

这里首先判断taskRemoved标志,这个标志为true则代表用户之前从最近任务界面里划掉了这个任务栈或者在最近任务界面里点击了清理,此时会调用Service.onTaskRemoved方法(从最近任务界面关闭应用,进程不一定会被杀死,而且Service具有死亡重建机制),在正常情况下则是调用Service.onStartCommand处理服务启动

当任务执行完成后,会调用AMS.serviceDoneExecuting方法告知,在这个方法中会根据onStartCommand的返回值(或执行完onTaskRemoved被赋值为START_TASK_REMOVED_COMPLETE),调整Service的死亡重建策略,并且会把之前的启动超时定时器取消

onStartCommand可以有以下几种返回值:

  • START_STICKY_COMPATIBILITYtargetSdkVersion < 5 (Android 2.0) 的App默认会返回这个,Service被杀后会被重建,但onStartCommand方法不会被执行
  • START_STICKYtargetSdkVersion >= 5 (Android 2.0) 的App默认会返回这个,Service被杀后会被重建,onStartCommand方法也会被执行,但此时onStartCommand方法的第一个参数Intentnull
  • START_NOT_STICKYService被杀后不会被重建
  • START_REDELIVER_INTENTService被杀后会被重建,onStartCommand方法也会被执行,此时onStartCommand方法的第一个参数IntentService被杀死前最后一次调用onStartCommand方法时传递的Intent

在这里我们只摆出结论,暂时不分析原理,如果感兴趣的话我会在后面的文章中再去详细分析

Context.bindService

到这里,Service通过startService路径的启动流程我们就基本分析完了,接着我们看另一条路径,bindService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//frameworks/base/core/java/android/app/ContextImpl.java
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,
getUser());
}

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, Executor executor, UserHandle user) {
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
//获取LoadedApk$ServiceDispatcher$IServiceConnection
//这个类是用来后续连接建立完成后发布连接,回调ServiceConnection各种方法的
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (handler != null && executor != null) {
throw new IllegalArgumentException("Handler and Executor both supplied");
}
if (mPackageInfo != null) {
if (executor != null) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
} else {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
}
} else {
throw new RuntimeException("Not supported in system context");
}
//确保Intent有效
validateServiceIntent(service);
try {
//获取ActivityRecord的远程Binder对象
IBinder token = getActivityToken();
//targetSdkVersion < 14 (Android 4.0)的情况下,如果没有设置BIND_AUTO_CREATE
//则该Service的优先级将会被视为等同于后台任务
if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
&& mPackageInfo.getApplicationInfo().targetSdkVersion
< android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
flags |= BIND_WAIVE_PRIORITY;
}
//跨进程准备
service.prepareToLeaveProcess(this);
//跨进程使用AMS绑定Service
int res = ActivityManager.getService().bindIsolatedService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
}
return res != 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}

这个方法主要就做了两件事:

  1. 创建或获取IServiceConnection:这里实际获取到的是LoadedApk中的内部类ServiceDispatcher中的内部类InnerConnection,这个类主要是用来后面建立或断开连接后,回调ServiceConnection接口的各个方法的
  2. 跨进程调用AMS.bindIsolatedService方法绑定Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String instanceName,
String callingPackage, int userId) throws TransactionTooLargeException {
enforceNotIsolatedCaller("bindService");

// Refuse possible leaked file descriptors
//校验Intent,不允许其携带fd
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}

//校验调用方包名
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}

// Ensure that instanceName, which is caller provided, does not contain
// unusual characters.
if (instanceName != null) {
for (int i = 0; i < instanceName.length(); ++i) {
char c = instanceName.charAt(i);
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9') || c == '_' || c == '.')) {
throw new IllegalArgumentException("Illegal instanceName");
}
}
}

synchronized(this) {
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, instanceName, callingPackage, userId);
}
}

这里仅仅做了一些简单的校验,然后将绑定服务的任务转交给了ActiveServices.bindServiceLocked方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String instanceName, String callingPackage, final int userId)
throws TransactionTooLargeException {
//获取调用方进程记录
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when binding service " + service);
}

ActivityServiceConnectionsHolder<ConnectionRecord> activity = null;
//token不为空表示是从Activity发起的,token实际为ActivityRecord的远程Binder对象
if (token != null) {
//获取Activity与Service的连接记录
activity = mAm.mAtmInternal.getServiceConnectionsHolder(token);
//ServiceConnectionsHolder为null说明调用方Activity不在栈中,直接异常返回
if (activity == null) {
return 0;
}
}

int clientLabel = 0;
PendingIntent clientIntent = null;
final boolean isCallerSystem = callerApp.info.uid == Process.SYSTEM_UID;

//如果调用方为系统级应用
if (isCallerSystem) {
// Hacky kind of thing -- allow system stuff to tell us
// what they are, so we can report this elsewhere for
// others to know why certain services are running.
service.setDefusable(true);
clientIntent = service.getParcelableExtra(Intent.EXTRA_CLIENT_INTENT);
if (clientIntent != null) {
clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);
if (clientLabel != 0) {
// There are no useful extras in the intent, trash them.
// System code calling with this stuff just needs to know
// this will happen.
service = service.cloneFilter();
}
}
}

//像对待Activity一样对待该Service
//需要校验调用方应用是否具有MANAGE_ACTIVITY_STACKS权限
if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
mAm.enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"BIND_TREAT_LIKE_ACTIVITY");
}

//此标志仅用于系统调整IMEs(以及与顶层App密切合作的其他跨进程的用户可见组件)的调度策略,仅限系统级App使用
if ((flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0 && !isCallerSystem) {
throw new SecurityException("Non-system caller (pid=" + Binder.getCallingPid()
+ ") set BIND_SCHEDULE_LIKE_TOP_APP when binding service " + service);
}

//允许绑定Service的应用程序管理白名单,仅限系统级App使用
if ((flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0 && !isCallerSystem) {
throw new SecurityException(
"Non-system caller " + caller + " (pid=" + Binder.getCallingPid()
+ ") set BIND_ALLOW_WHITELIST_MANAGEMENT when binding service " + service);
}

//允许绑定到免安装应用提供的服务,仅限系统级App使用
if ((flags & Context.BIND_ALLOW_INSTANT) != 0 && !isCallerSystem) {
throw new SecurityException(
"Non-system caller " + caller + " (pid=" + Binder.getCallingPid()
+ ") set BIND_ALLOW_INSTANT when binding service " + service);
}

//允许Service后台启动Activity
//需要校验调用方应用是否具有START_ACTIVITIES_FROM_BACKGROUND权限
if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
mAm.enforceCallingPermission(
android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
"BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS");
}

//判断调用方是否为前台
final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;

//查找相应的Service
ServiceLookupResult res =
retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true,
callerFg, isBindExternal, allowInstant);
if (res == null) {
return 0;
}
if (res.record == null) {
return -1;
}
ServiceRecord s = res.record;
boolean permissionsReviewRequired = false;

// If permissions need a review before any of the app components can run,
// we schedule binding to the service but do not start its process, then
// we launch a review activity to which is passed a callback to invoke
// when done to start the bound service's process to completing the binding.
//如果需要用户手动确认授权
if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
s.packageName, s.userId)) {

permissionsReviewRequired = true;

// Show a permission review UI only for binding from a foreground app
//只有调用方进程在前台才可以显示授权弹窗
if (!callerFg) {
return 0;
}

final ServiceRecord serviceRecord = s;
final Intent serviceIntent = service;

//用户手动确认授权后执行的回调
RemoteCallback callback = new RemoteCallback(
new RemoteCallback.OnResultListener() {
@Override
public void onResult(Bundle result) {
synchronized(mAm) {
final long identity = Binder.clearCallingIdentity();
try {
if (!mPendingServices.contains(serviceRecord)) {
return;
}
// If there is still a pending record, then the service
// binding request is still valid, so hook them up. We
// proceed only if the caller cleared the review requirement
// otherwise we unbind because the user didn't approve.
//二次检查权限
if (!mAm.getPackageManagerInternalLocked()
.isPermissionsReviewRequired(
serviceRecord.packageName,
serviceRecord.userId)) {
try {
//拉起服务,如果服务未创建,则会创建服务并调用其onCreate方法
//如果服务已创建则什么都不会做
bringUpServiceLocked(serviceRecord,
serviceIntent.getFlags(),
callerFg, false, false);
} catch (RemoteException e) {
/* ignore - local call */
}
} else {
//无相应权限则解绑Service
unbindServiceLocked(connection);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
});

final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_MULTIPLE_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, s.packageName);
intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);

//弹出授权弹窗
mAm.mHandler.post(new Runnable() {
@Override
public void run() {
mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));
}
});
}

final long origId = Binder.clearCallingIdentity();

try {
//取消之前的Service重启任务(如果有)
if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "BIND SERVICE WHILE RESTART PENDING: "
+ s);
}

if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
//如果是第一次绑定的话,设置跟踪器
if (!s.hasAutoCreateConnections()) {
// This is the first binding, let the tracker know.
ServiceState stracker = s.getTracker();
if (stracker != null) {
stracker.setBound(true, mAm.mProcessStats.getMemFactorLocked(),
s.lastActivity);
}
}
}

//绑定的服务代表受保护的系统组件,因此必须对其应用关联做限制
if ((flags & Context.BIND_RESTRICT_ASSOCIATIONS) != 0) {
mAm.requireAllowedAssociationsLocked(s.appInfo.packageName);
}

//建立调用方与服务方之间的关联
mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
callerApp.getCurProcState(), s.appInfo.uid, s.appInfo.longVersionCode,
s.instanceName, s.processName);
// Once the apps have become associated, if one of them is caller is ephemeral
// the target app should now be able to see the calling app
mAm.grantImplicitAccess(callerApp.userId, service,
callerApp.uid, UserHandle.getAppId(s.appInfo.uid));

//查询App绑定信息
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
//创建连接信息
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent,
callerApp.uid, callerApp.processName, callingPackage);

IBinder binder = connection.asBinder();
//添加连接
s.addConnection(binder, c);
b.connections.add(c);
if (activity != null) {
activity.addConnection(c);
}
b.client.connections.add(c);
//建立关联
c.startAssociationIfNeeded();
//表示此服务比发起绑定的应用重要性更高
if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
b.client.hasAboveClient = true;
}
//允许绑定Service的应用程序管理白名单
if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
s.whitelistManager = true;
}
//允许Service后台启动Activity
if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
s.setHasBindingWhitelistingBgActivityStarts(true);
}
//更新是否有与Service建立连接的Activity
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
}
//更新连接列表
ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
if (clist == null) {
clist = new ArrayList<>();
mServiceConnections.put(binder, clist);
}
clist.add(c);

//绑定存在就会自动创建服务
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
//拉起服务,如果服务未创建,则会创建服务并调用其onCreate方法
//如果服务已创建则什么都不会做
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired) != null) {
return 0;
}
}

//检查是否允许前台服务使用while-in-use权限
if (!s.mAllowWhileInUsePermissionInFgs) {
s.mAllowWhileInUsePermissionInFgs =
shouldAllowWhileInUsePermissionInFgsLocked(callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(),
service, s, false);
}

//更新flag以及进程优先级
if (s.app != null) {
if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
s.app.treatLikeActivity = true;
}
if (s.whitelistManager) {
s.app.whitelistManager = true;
}
// This could have made the service more important.
mAm.updateLruProcessLocked(s.app,
(callerApp.hasActivitiesOrRecentTasks() && s.app.hasClientActivities())
|| (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP
&& (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
b.client);
mAm.updateOomAdjLocked(s.app, OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
}

if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
//如果服务之前就已经在运行,即Service.onBind方法已经被执行,返回的IBinder对象也已经被保存
//调用LoadedApk$ServiceDispatcher$InnerConnection.connected方法
//回调ServiceConnection.onServiceConnected方法
c.conn.connected(s.name, b.intent.binder, false);

// If this is the first app connected back to this binding,
// and the service had previously asked to be told when
// rebound, then do so.
//当服务解绑,调用到Service.onUnbind方法时返回true,此时doRebind变量就会被赋值为true
//此时,当再次建立连接时,服务会回调Service.onRebind方法
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
//如果服务是因这次绑定而创建的
//请求执行Service.onBind方法,获取返回的IBinder对象
//发布Service,回调ServiceConnection.onServiceConnected方法
requestServiceBindingLocked(s, b.intent, callerFg, false);
}

maybeLogBindCrossProfileService(userId, callingPackage, callerApp.info.uid);

//将此Service从正在后台启动服务列表和延迟启动服务列表中移除
//如果正在后台启动服务列表中存在此服务的话,将之前设置为延迟启动的服务调度出来后台启动
getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);

} finally {
Binder.restoreCallingIdentity(origId);
}

//返回值大于0则视为成功
return 1;
}

这个方法做的事就比较多了,我们挑重点来说:

  • 获取或创建各种连接记录(ActivityServiceConnectionsHolderAppBindRecordConnectionRecord等)

  • 校验各种flags

  • 查找相应的Service

  • 检查是否需要用户手动确认权限并弹出权限确认弹窗

  • 向各个连接记录类中添加ConnectionRecord连接信息

  • 如果flags设置了BIND_AUTO_CREATE便会调用bringUpServiceLocked方法尝试拉起服务,如果服务未创建,则会创建服务并调用其onCreate方法,如果服务已创建,则什么都不会做:这里和startService路径一样都调用到了bringUpServiceLocked方法,但最终调用的结果却不太一样,这是因为startService路径中,ServiceRecord.startRequestedtrue并且向ServiceRecord.pendingStarts中添加了启动项,而bindService路径不会向ServiceRecord.pendingStarts中添加启动项,并且由于ServiceRecord.startRequestedfalse,因此也不会去添加假的启动项,所以和startService不同,最终不会回调Service.onStartCommand方法

  • 如果服务之前就已经在运行,则表示Service.onBind方法已经被执行,返回的IBinder对象也已经被保存,此时直接调用LoadedApk$ServiceDispatcher$InnerConnection.connected方法,在这个方法中会回调ServiceConnection.onServiceConnected方法

  • 如果服务是因这次绑定而创建的,则调用requestServiceBindingLocked方法请求执行Service.onBind方法,获取返回的IBinder对象,然后发布Service,回调ServiceConnection.onServiceConnected方法

  • 最后调用ActiveServices$ServiceMap.ensureNotStartingBackgroundLocked方法继续调度后台Service的启动

bringUpServiceLocked方法我们之前已经分析过了,我们接下来看服务创建后所要调用的requestServiceBindingLocked方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
if (r.app == null || r.app.thread == null) {
// If service is not currently running, can't yet bind.
return false;
}
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
//记录Service执行操作并设置超时回调
//前台服务超时时间为20s,后台服务超时时间为200s
bumpServiceExecutingLocked(r, execInFg, "bind");
//设置进程状态
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
//回到App进程,调度执行Service的bind操作
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.getReportedProcState());
//请求绑定完成
if (!rebind) {
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
} catch (TransactionTooLargeException e) {
// Keep the executeNesting count accurate.
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
throw e;
} catch (RemoteException e) {
// Keep the executeNesting count accurate.
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
return false;
}
}
return true;
}

和之前一样,这里也是调用App进程中的ActivityThread$ApplicationThread.scheduleBindService方法进行绑定操作

1
2
3
4
5
6
7
8
9
10
11
12
//frameworks/base/core/java/android/app/ActivityThread.java
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
//更新进程信息
updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
s.token = token;
s.intent = intent;
s.rebind = rebind;

sendMessage(H.BIND_SERVICE, s);
}

将绑定Service的必要信息包装成BindServiceData对象后,通过Handler依次发送Message处理服务启动,这里最终调用的是ActivityThread.handleBindService方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//frameworks/base/core/java/android/app/ActivityThread.java
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
if (!data.rebind) {
//正常情况下回调Service.onBind方法,获得控制Service的IBinder对象
IBinder binder = s.onBind(data.intent);
//发布Service
ActivityManager.getService().publishService(
data.token, data.intent, binder);
} else {
//当服务解绑,调用到Service.onUnbind方法时返回true,此时doRebind变量就会被赋值为true
//此时,当再次建立连接时,服务会回调Service.onRebind方法
s.onRebind(data.intent);
//Service相关任务执行完成
//这一步中会把之前的启动超时定时器取消
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to bind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}

在这个方法里面调用了Service.onBind方法,获得到了控制ServiceIBinder对象,然后再调用AMS.publishService发布服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void publishService(IBinder token, Intent intent, IBinder service) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}

synchronized(this) {
if (!(token instanceof ServiceRecord)) {
throw new IllegalArgumentException("Invalid service token");
}
//转交给ActiveServices处理
mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}
}

//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
final long origId = Binder.clearCallingIdentity();
try {
if (r != null) {
Intent.FilterComparison filter
= new Intent.FilterComparison(intent);
//获取Intent绑定记录
IntentBindRecord b = r.bindings.get(filter);
if (b != null && !b.received) {
//保存控制Service的IBinder对象
b.binder = service;
//请求绑定完成
b.requested = true;
//IBinder对象获取完成
b.received = true;
//遍历所有与此服务绑定的客户端连接
ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> clist = connections.valueAt(conni);
for (int i=0; i<clist.size(); i++) {
ConnectionRecord c = clist.get(i);
if (!filter.equals(c.binding.intent.intent)) {
continue;
}
//调用LoadedApk$ServiceDispatcher$IServiceConnection.connected方法
//回调ServiceConnection.onServiceConnected方法
c.conn.connected(r.name, service, false);
}
}
}
//Service相关任务执行完成
//这一步中会把之前的启动超时定时器取消
serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}

在这个方法中,首先将通过Service.onBind获得到的控制ServiceIBinder对象保存在IntentBindRecord中,这样之后再有其他client绑定服务,就只需要用它作为参数回调ServiceConnection.onServiceConnected方法就可以了

接下来遍历所有与此服务绑定的客户端连接,对符合条件的连接执行LoadedApk$ServiceDispatcher$IServiceConnection.connected方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//frameworks/base/core/java/android/app/LoadedApk.java
public void connected(ComponentName name, IBinder service, boolean dead)
throws RemoteException {
//mDispatcher是对ServiceDispatcher的弱引用
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
//调用ServiceDispatcher.connected方法
sd.connected(name, service, dead);
}
}

public void connected(ComponentName name, IBinder service, boolean dead) {
//RunConnection里也是调用了doConnected方法
if (mActivityExecutor != null) {
mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
} else if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 0, dead));
} else {
doConnected(name, service, dead);
}
}

public void doConnected(ComponentName name, IBinder service, boolean dead) {
ServiceDispatcher.ConnectionInfo old;
ServiceDispatcher.ConnectionInfo info;

synchronized (this) {
if (mForgotten) {
// We unbound before receiving the connection; ignore
// any connection received.
return;
}
old = mActiveConnections.get(name);
//如果旧的连接信息中的IBinder对象和本次调用传入的IBinder对象是同一个对象
if (old != null && old.binder == service) {
// Huh, already have this one. Oh well!
return;
}

if (service != null) {
// A new service is being connected... set it all up.
//建立一个新的连接信息
info = new ConnectionInfo();
info.binder = service;
info.deathMonitor = new DeathMonitor(name, service);
try {
//注册Binder死亡通知
service.linkToDeath(info.deathMonitor, 0);
//保存本次连接信息
mActiveConnections.put(name, info);
} catch (RemoteException e) {
// This service was dead before we got it... just
// don't do anything with it.
//服务已死亡,移除连接信息
mActiveConnections.remove(name);
return;
}
} else {
// The named service is being disconnected... clean up.
mActiveConnections.remove(name);
}

//移除Binder死亡通知
if (old != null) {
old.binder.unlinkToDeath(old.deathMonitor, 0);
}
}

// If there was an old service, it is now disconnected.
//回调ServiceConnection.onServiceDisconnected
//通知client之前的连接已被断开
if (old != null) {
mConnection.onServiceDisconnected(name);
}
//如果Service死亡需要回调ServiceConnection.onBindingDied通知client服务死亡
if (dead) {
mConnection.onBindingDied(name);
}
// If there is a new viable service, it is now connected.
if (service != null) {
//回调ServiceConnection.onServiceConnected方法
//告知client已建立连接
mConnection.onServiceConnected(name, service);
} else {
// The binding machinery worked, but the remote returned null from onBind().
//当Service.onBind方法返回null时,回调ServiceConnection.onNullBinding方法
mConnection.onNullBinding(name);
}
}

可以看到,在这个方法里最终执行了ServiceConnection.onServiceConnected回调,通知客户端已与Service建立连接

至此,整个bindService的流程就结束了

总结

Service的整个启动流程到这里基本上都分析完了,至于Service的停止,重建等流程,我将会在后面的文章中再慢慢分析


谈谈Android如何实现不同大小的圆角

简介

在开发过程中,设计常常会有一些比较炫酷的想法,比如两边不一样大小的圆角啦,甚至四角的radius各不相同,对于这种情况我们该怎么实现呢?

背景圆角

Shape

对于一般的背景,我们可以直接使用shape,这种方法天生支持设置四角不同的radius,比如:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#8358FF" />
<corners
android:bottomLeftRadius="10dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="30dp"
android:topRightRadius="40dp" />
</shape>

小贴士:shape在代码层的实现为GradientDrawable,可以直接在代码层构建圆角背景,顺便推荐一下我写的库:ShapeLayout,可以方便的实现shape背景,告别xml

内容圆角

很多情况下,设置背景的四边不同圆角并不能满足我们,大多数情况下,我们需要连着里面的内容一起切圆角,这里我们需要先指正一下网上的一个错误写法

有人发文说,可以通过outline.setConvexPath方法,实现四角不同radius,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
outline?.setConvexPath(
Path().apply {
addRoundRect(
0f, 0f, width.toFloat(), height.toFloat(),
floatArrayOf(
topLeftRadius,
topLeftRadius,
topRightRadius,
topRightRadius,
bottomRightRadius,
bottomRightRadius,
bottomLeftRadius,
bottomLeftRadius
),
Path.Direction.CCW
)
}
)

经过实测,这样写是不行的,准确的来说,在大部分系统上是不行的(MIUI上可以,我不知道是该夸它兼容性太好了还是该吐槽它啥,我的测试机用的小米,这导致我在最后的测试阶段才发现这个问题)

指出错误方法后,让我们来看看正确解法有哪些

CardView

说到切内容圆角,我们自然而然会去想到CardView,其实CardView的圆角也是通过Outline实现的

有人可能要问了,CardView不是只支持四角相同radius吗?别急,且看我灵机一动想出来的神奇嵌套大法

神奇嵌套大法

既然一个CardView只能设一个radius,那我多用几个CardView嵌套是否能解决问题呢?

举个最简单的例子,比如说设计想要上半部分为12dp的圆角,下半部分没有圆角,我们需要一个辅助View,让他的顶部和父布局的底部对齐,然后设置成圆角大小的高度或者margin,接着使用CardView,让它的底部对齐这个辅助View的底部,再设置一个圆角大小的padding,这样,由于CardView超出了父布局的边界,所以底部的圆角不会显示出来,再由于我们设置了恰好的padding,所以CardView里面的内容也能完整展示,可谓完美,实例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<Space
android:id="@+id/guideline"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="parent" />

<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="12dp"
app:cardElevation="0dp"
app:contentPaddingBottom="12dp"
app:layout_constraintBottom_toBottomOf="@+id/guideline">

<ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:adjustViewBounds="true"
android:background="#8358FF" />

</androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

上面的例子没有嵌套,因为另一边没有圆角,那么如果我们需要上半部分为12dp的圆角,下半部分为6dp的圆角,我们可以这样操作

手法和上面的例子一样,不过我们在最外层再嵌套一个CardView,并且将其圆角设为较小的那个圆角大小6dp,将里面的CardView的圆角设置成较大的那个圆角大小12dp,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="6dp"
app:cardElevation="0dp"
app:layout_constraintTop_toTopOf="parent">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<Space
android:id="@+id/guideline"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="parent" />

<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="12dp"
app:cardElevation="0dp"
app:contentPaddingBottom="12dp"
app:layout_constraintBottom_toTopOf="@+id/guideline">

<ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:adjustViewBounds="true"
android:background="#8358FF" />

</androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

本质上就是大圆角套小圆角,大圆角的裁切范围更大,会覆盖小圆角裁切的范围,从视觉上看就实现了两边的不同圆角

那么如果我们想进一步实现三边不同圆角或者四边不同圆角呢?原理和上面是一样的,只不过嵌套和占位会变得更加复杂,记住一个原则,小圆角在外,大圆角在内即可,我直接把具体实现贴在下面,各位自取即可:

  • 三边不同圆角(左下6dp,左上12dp,右上24dp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<Space
android:id="@+id/guideline"
android:layout_width="6dp"
android:layout_height="match_parent"
app:layout_constraintStart_toEndOf="parent" />

<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="6dp"
app:cardElevation="0dp"
app:contentPaddingRight="6dp"
app:layout_constraintEnd_toEndOf="@+id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<Space
android:id="@+id/guideline2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="parent" />

<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="12dp"
app:cardElevation="0dp"
app:contentPaddingBottom="12dp"
app:layout_constraintBottom_toTopOf="@+id/guideline2">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<Space
android:id="@+id/guideline3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="24dp"
app:layout_constraintEnd_toStartOf="parent" />

<Space
android:id="@+id/guideline4"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="parent" />

<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="24dp"
app:cardElevation="0dp"
app:contentPaddingBottom="24dp"
app:contentPaddingLeft="24dp"
app:layout_constraintBottom_toBottomOf="@+id/guideline4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline3">

<ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:adjustViewBounds="true"
android:background="#8358FF" />

</androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>
  • 四边不同圆角(左下6dp,左上12dp,右上24dp,右下48dp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<Space
android:id="@+id/guideline"
android:layout_width="6dp"
android:layout_height="match_parent"
app:layout_constraintStart_toEndOf="parent" />

<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="6dp"
app:cardElevation="0dp"
app:contentPaddingRight="6dp"
app:layout_constraintEnd_toEndOf="@+id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<Space
android:id="@+id/guideline2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="parent" />

<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="12dp"
app:cardElevation="0dp"
app:contentPaddingBottom="12dp"
app:layout_constraintBottom_toTopOf="@+id/guideline2">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<Space
android:id="@+id/guideline3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="24dp"
app:layout_constraintEnd_toStartOf="parent" />

<Space
android:id="@+id/guideline4"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="parent" />

<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="24dp"
app:cardElevation="0dp"
app:contentPaddingBottom="24dp"
app:contentPaddingLeft="24dp"
app:layout_constraintBottom_toBottomOf="@+id/guideline4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline3">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<Space
android:id="@+id/guideline5"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="48dp"
app:layout_constraintEnd_toStartOf="parent" />

<Space
android:id="@+id/guideline6"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="48dp"
app:layout_constraintBottom_toTopOf="parent" />

<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="48dp"
app:cardElevation="0dp"
app:contentPaddingLeft="48dp"
app:contentPaddingTop="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline5"
app:layout_constraintTop_toTopOf="@+id/guideline6">

<ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:adjustViewBounds="true"
android:background="#8358FF" />

</androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

自定义ImageView

由于大部分裁切内容的需求,其中的内容都是图片,所以我们也可以直接对图片进行裁切,此时我们就可以自定义ImageView来将图片裁剪出不同大小的圆角

clipPath

先说这个方法的缺点,那就是无法使用抗锯齿,这一点缺陷注定了它无法被正式使用,但我们还是来看看他是如何实现的

首先,我们需要重写ImageViewonSizeChanged方法,为我们的Path确定路线

1
2
3
4
5
6
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
path.reset()
//这里的radii便是我们自定义的四边圆角大小的数组(size为8,从左上顺时针到左下)
path.addRoundRect(0f, 0f, w.toFloat(), h.toFloat(), radii, Path.Direction.CW)
}

接着我们重写onDraw方法

1
2
3
4
override fun onDraw(canvas: Canvas) {
canvas.clipPath(path)
super.onDraw(rawBitmapCanvas)
}

网上有的教程说要设置PaintFlagsDrawFilter,但实际上就算为这个PaintFlagsDrawFilter设置了Paint.ANTI_ALIAS_FLAG抗锯齿属性也没用,抗锯齿只在使用了Paint的情况下才可以生效

PorterDuff

既然clipPath无法使用抗锯齿,那我们可以换一条路线曲线救国,那就是使用PorterDuff

当然,这种方法也有它的缺点,那就是不能使用硬件加速,但相比无法使用抗锯齿而言,这点缺点也就不算什么了

首先,我们要在构造方法中禁用硬件加速

1
2
3
init {
setLayerType(LAYER_TYPE_SOFTWARE, null)
}

然后重写onSizeChanged方法,在这个方法中,我们需要确定Path,构造出相应大小的BitmapCanvas,这俩是用来获取原始无圆角的Bitmap

1
2
3
4
5
6
7
8
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
path.reset()
path.addRoundRect(0f, 0f, w.toFloat(), h.toFloat(), radii, Path.Direction.CW)

rawBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
rawBitmapCanvas = Canvas(rawBitmap!!)
}

接着我们重写onDraw方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)

override fun onDraw(canvas: Canvas) {
val rawBitmap = rawBitmap ?: return
val rawBitmapCanvas = rawBitmapCanvas ?: return

super.onDraw(rawBitmapCanvas)

canvas.drawPath(path, paint)
paint.xfermode = xfermode
canvas.drawBitmap(rawBitmap, 0f, 0f, paint)
paint.xfermode = null
}

这里,我们调用父类的onDraw方法,获取到原始无圆角的Bitmap,然后绘制Path,再通过PorterDuff的叠加效果绘制我们刚刚得到的原始Bitmap,由于PorterDuff.Mode.SRC_IN的效果是取两层绘制交集,显示上层,所以我们最终便获得了一个带圆角的图片

BitmapShader

有人指出,可以使用BitmapShader方案,我去实测了一下,确实可以在开启了硬件加速的情况下使用,目前看上去似乎没有什么缺点,在此感谢评论区的大神们,这种方案实现起来也很简单,和上面的差不多

首先重写onSizeChanged方法

1
2
3
4
5
6
7
8
9
10
11
12
private var bitmapShader: BitmapShader? = null

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
path.reset()
path.addRoundRect(0f, 0f, w.toFloat(), h.toFloat(), radii, Path.Direction.CW)

rawBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
rawBitmapCanvas = Canvas(rawBitmap!!)
bitmapShader = BitmapShader(rawBitmap!!, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
paint.shader = bitmapShader
}

然后onDraw方法就更简单了

1
2
3
4
5
override fun onDraw(canvas: Canvas) {
val rawBitmapCanvas = rawBitmapCanvas ?: return
super.onDraw(rawBitmapCanvas)
canvas.drawPath(path, paint)
}

截图问题

如果想要将View截图成Bitmap,在Android 8.0及以上系统中我们可以使用PixelCopy,此时使用CardViewOutline裁切的圆角不会有任何问题,而在Android 8.0以下的系统中,通常我们是构建一个带BitmapCanvas,然后对要截图的View调用draw方法达成截图效果,而在这种情况下,使用CardViewOutline裁切的圆角便会出现无效的情况(截图出来的Bitmap中,圆角没了),这种情况的出现似乎也和硬件加速有关,针对这种问题,如果是图片圆角的情况,建议直接使用BitmapShader方案,这样无论使用哪种方式截图都不会出现问题

这里顺便把截图的代码也贴一下吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
fun View.screenshot(activity: Activity?, onSuccess: (Bitmap) -> Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && activity != null) {
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)

val locationOfViewInWindow = IntArray(2)
this.getLocationInWindow(locationOfViewInWindow)

PixelCopy.request(
activity.window,
Rect(
locationOfViewInWindow[0],
locationOfViewInWindow[1],
locationOfViewInWindow[0] + width,
locationOfViewInWindow[1] + height
),
bitmap, { copyResult ->
if (copyResult == PixelCopy.SUCCESS) {
onSuccess(bitmap)
} else {
screenshotBackup(onSuccess)
}
},
Handler(Looper.getMainLooper())
)
} else {
screenshotBackup(onSuccess)
}
}

private fun View.screenshotBackup(onSuccess: (Bitmap) -> Unit) {
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
draw(canvas)
onSuccess(bitmap)
}

总结

以上就是我本人目前对Android实现不同大小的圆角的一些想法和遇到的问题,至于CardView嵌套会不会带来什么性能问题,我用BitmapShader方案做了一下对比,不管加载速度,还是内存占用,都没有发现明显差别(甚至用CardView嵌套速度还快点?),所以各位不用担心性能问题,选适合自己的方案就行了,各位小伙伴有什么更好的解决方案,欢迎在评论区指出,大家一起集思广益


Android源码分析 - Framework层的ContentProvider全解析

开篇

本篇以android-11.0.0_r25作为基础解析

在四大组件中,可能我们平时用到最少的便是ContentProvider了,ContentProvider是用来帮助应用管理其自身和其他应用所存储数据的访问,并提供与其他应用共享数据的方法,使用ContentProvider可以安全的在应用之间共享和修改数据,比如说访问图库,通讯录等

在之前的文章中,我们提到了ContentProvider的启动时机,不妨顺水推舟,干脆把这一块分析个明白,本篇文章并不会教大家怎样使用ContentProvider,只将精力集中在ContentProvider在系统层面的启动与交互上

基础知识

ContentResolver

想要通过ContentProvider访问应用数据,我们通常需要借助ContentResolver的API,我们可以通过Context.getContentResolver方法获取其实例对象

ContentResolver是一个抽象类,它的抽象方法由ContextImpl.ApplicationContentResolver继承实现,我们实际上获取到的也是这个实例对象

Uri格式

ContentProvider的使用需要先获得提供者的Uri,它的格式如下:

  1. Scheme:固定为content://
  2. Authority:为提供者在AndroidManifest里设置的android:authorities属性
  3. 资源相对路径
  4. 资源ID

其中,资源相对路径和资源ID不是必须的,要看资源存储的数量及形式

举个栗子,外部存储中某张图片的Uri为:content://media/external/images/media/${id},其中media为Authority,/external/images/media为外部存储图片的相对路径,id为这张图片资源在数据库中存储的id

获取ContentProvider

ContentProvider作为共享数据的桥梁,最主要的几个功能无非是增、删、改、查,我们就以查作为入口来分析ContentProvider对象是怎么获取的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//ContentResolver.query
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
...

//尝试获取unstableProvider
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
...
try {
//调用远程对象query
qCursor = unstableProvider.query(mPackageName, mAttributionTag, uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
//尝试获取stableProvider
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
//调用远程对象query
qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection,
queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}

// Force query execution. Might fail and throw a runtime exception here.
qCursor.getCount();
...

// Wrap the cursor object into CursorWrapperInner object.
//将qCursor和provider包装成CursorWrapperInner对象返回
final IContentProvider provider = (stableProvider != null) ? stableProvider
: acquireProvider(uri);
final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
stableProvider = null;
qCursor = null;
return wrapper;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} finally {
//释放资源
if (qCursor != null) {
qCursor.close();
}
if (cancellationSignal != null) {
cancellationSignal.setRemote(null);
}
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
if (stableProvider != null) {
releaseProvider(stableProvider);
}
}
}

我们可以将这个方法大致分成以下几个步骤:

  1. 获取unstableProvider远程对象
  2. 调用unstableProvider对象的query方法,获取qCursor
  3. 如果query过程中远程对象死亡,尝试获取stableProvider并调用query方法获取qCursor
  4. 获取stableProvider(如果之前没获取的话)
  5. qCursorstableProvider包装成CursorWrapperInner对象返回
  6. 释放资源

既然ContentProvider可以在应用之前共享数据,那它必然是支持跨进程的,没错,用的还是我们熟悉的Binder通信,IContentProvider对象即是给调用方进程使用的远程Binder对象,回顾这个方法我们发现,IContentProvider远程对象是通过acquireUnstableProvideracquireProvider获取的,我们接下来看看这两个方法做了什么

这里有一个关于unstablestable的概念,对于通过这两种方式获取的ContentProvider分别会有unstableCountstableCount两种引用计数,如果远程ContentProvider所在进程死亡,且其stableCount > 0的话,则会将其通过stable方式关联的调用方进程一同杀死,具体的流程我们会在后面分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
return acquireUnstableProvider(mContext, uri.getAuthority());
}
return null;
}

public final IContentProvider acquireProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
final String auth = uri.getAuthority();
if (auth != null) {
return acquireProvider(mContext, auth);
}
return null;
}

public final IContentProvider acquireUnstableProvider(String name) {
if (name == null) {
return null;
}
return acquireUnstableProvider(mContext, name);
}

public final IContentProvider acquireProvider(String name) {
if (name == null) {
return null;
}
return acquireProvider(mContext, name);
}

// ContextImpl.ApplicationContentResolver 内实现
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}

// ContextImpl.ApplicationContentResolver 内实现
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}

ActivityThread.acquireProvider

Android系统是通过Authority来区分不同的ContentProvider的,经过一些简单的判断处理后,最终调用了ActivityThread.acquireProvider方法去获取ContentProvider,而acquireUnstableProvideracquireProvider的区别只是最后一个布尔值入参不同罢了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
//尝试从本地缓存中获取ContentProvider对象
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}

// There is a possible race here. Another thread may try to acquire
// the same provider at the same time. When this happens, we want to ensure
// that the first one wins.
// Note that we cannot hold the lock while acquiring and installing the
// provider since it might take a long time to run and it could also potentially
// be re-entrant in the case where the provider is in the same process.
ContentProviderHolder holder = null;
try {
synchronized (getGetProviderLock(auth, userId)) {
//使用AMS获取ContentProvider对象
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
...
return null;
}

// Install provider will increment the reference count for us, and break
// any ties in the race.
//安装ContentProvider
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}

这个方法大概做了以下几件事:

  1. 首先从缓存中尝试获取IContentProvider对象
  2. 使用AMS获取ContentProviderHolder对象
  3. 安装ContentProvider
  4. 返回IContentProvider对象

ActivityThread.acquireExistingProvider

我们首先看通过acquireExistingProvider方法尝试从缓存中获取IContentProvider对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
synchronized (mProviderMap) {
//从缓存Map中查找
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
}

IContentProvider provider = pr.mProvider;
IBinder jBinder = provider.asBinder();
//判断远端进程是否已被杀死
if (!jBinder.isBinderAlive()) {
// The hosting process of the provider has died; we can't
// use this one.
//清理ContentProvider
handleUnstableProviderDiedLocked(jBinder, true);
return null;
}

// Only increment the ref count if we have one. If we don't then the
// provider is not reference counted and never needs to be released.
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
//更新引用计数
incProviderRefLocked(prc, stable);
}
return provider;
}
}

首先通过AuthorityuserId来从Map中查找是否已存在对应的ProviderClientRecord对象,然后从中取出IContentProvider对象,再检查其中的远程Binder对象是否已被杀死,最后一切无误,增加ContentProvider的引用计数

AMS.getContentProvider

如果这一步没有获取到,程序会继续从AMS获取ContentProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String callingPackage, String name, int userId,
boolean stable) {
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
+ name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
final int callingUid = Binder.getCallingUid();
if (callingPackage != null && mAppOpsService.checkPackage(callingUid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
throw new SecurityException("Given calling package " + callingPackage
+ " does not match caller's uid " + callingUid);
}
return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
null, stable, userId);
}

经过一些检查后调用getContentProviderImpl方法,这个方法有点长,我们分段来看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;

synchronized(this) {
//获取调用方所在进程记录
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
}

boolean checkCrossUser = true;

// First check if this content provider has been published...
//检查需要的ContentProvider是否已被发布
cpr = mProviderMap.getProviderByName(name, userId);
// If that didn't work, check if it exists for user 0 and then
// verify that it's a singleton provider before using it.
//如果没找到,尝试从系统用户中查找已发布的ContentProvider
//并确保它是可用的单例组件,条件如下:
//是用户级应用程序且组件设置了单例flag且拥有INTERACT_ACROSS_USERS权限 或 App运行在system进程中 或 组件设置了单例flag且是同一个App
if (cpr == null && userId != UserHandle.USER_SYSTEM) {
cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
if (cpr != null) {
cpi = cpr.info;
if (isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r == null ? callingUid : r.uid,
cpi.applicationInfo.uid)) {
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else {
cpr = null;
cpi = null;
}
}
}

//判断ContentProvider所在进程是否已死亡
ProcessRecord dyingProc = null;
if (cpr != null && cpr.proc != null) {
providerRunning = !cpr.proc.killed;

// Note if killedByAm is also set, this means the provider process has just been
// killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called
// yet. So we need to call appDiedLocked() here and let it clean up.
// (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
// how to test this case.)
if (cpr.proc.killed && cpr.proc.killedByAm) {
Slog.wtf(TAG, cpr.proc.toString() + " was killed by AM but isn't really dead");
// Now we are going to wait for the death before starting the new process.
dyingProc = cpr.proc;
}
}
...
}
...
}

首先,第一部分,检查目标ContentProvider是否已被发布并记录在了mProviderMap中,注意这里的mProviderMapAMS中的一个成员变量,一系列Map的一个集合,和ActivityThread中的mProviderMap不是一个东西。如果在当前用户中找不到,且当前用户不是系统用户(UserHandle.USER_SYSTEM == 0),则尝试从系统用户中查找合法可用的单例ContentProvider,符合以下任一一个条件的ContentProvider即可被视作单例ContentProvider

  • App是用户级应用程序(uid >= 10000)且ContentProvider组件设置了单例flag(android:singleUser)且App拥有INTERACT_ACROSS_USERS权限
  • App运行在system进程中
  • ContentProvider组件设置了单例flag(android:singleUser)且是同一个App

至于为什么跨用户访问需要单例这个条件,这个和多用户相关,我也不是很清楚,以后如果分析到了多用户这块再回来补充。目前国内厂商的应用分身、手机分身功能大部分用的就是多用户技术

接着通过目标ContentProviderRecord是否存在和其所在进程是否还存活判断目标ContentProvider是否在运行中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;

synchronized(this) {
...
//ContentProvider正在运行中
if (providerRunning) {
cpi = cpr.info;

//如果此ContentProvider可以在调用者进程中直接运行(同一个App的同进程 或 同一个App且Provider组件支持多进程)
//直接返回一个新的ContentProviderHolder让调用者进程自己启动ContentProvider
if (r != null && cpr.canRunHere(r)) {
... //权限检查

// This provider has been published or is in the process
// of being published... but it is also allowed to run
// in the caller's process, so don't make a connection
// and just let the caller instantiate its own instance.
ContentProviderHolder holder = cpr.newHolder(null);
// don't give caller the provider object, it needs
// to make its own.
holder.provider = null;
return holder;
}

// Don't expose providers between normal apps and instant apps
try {
if (AppGlobals.getPackageManager()
.resolveContentProvider(name, 0 /*flags*/, userId) == null) {
return null;
}
} catch (RemoteException e) {
}

... //权限检查

final long origId = Binder.clearCallingIdentity();

// In this case the provider instance already exists, so we can
// return it right away.
//获取连接并更新引用计数
conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
stable);
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
if (cpr.proc != null
&& r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
// If this is a perceptible app accessing the provider,
// make sure to count it as being accessed and thus
// back up on the LRU list. This is good because
// content providers are often expensive to start.
//更新进程优先级
mProcessList.updateLruProcessLocked(cpr.proc, false, null);
}
}

final int verifiedAdj = cpr.proc.verifiedAdj;
//更新进程adj
boolean success = updateOomAdjLocked(cpr.proc, true,
OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
// XXX things have changed so updateOomAdjLocked doesn't actually tell us
// if the process has been successfully adjusted. So to reduce races with
// it, we will check whether the process still exists. Note that this doesn't
// completely get rid of races with LMK killing the process, but should make
// them much smaller.
if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
success = false;
}
maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
// NOTE: there is still a race here where a signal could be
// pending on the process even though we managed to update its
// adj level. Not sure what to do about this, but at least
// the race is now smaller.
if (!success) {
// Uh oh... it looks like the provider's process
// has been killed on us. We need to wait for a new
// process to be started, and make sure its death
// doesn't kill our process.
Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
//ContentProvider所在进程被杀了,更新引用计数
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
//仍有别的地方对这个ContentProvider有引用,直接返回null(需要等待进程清理干净才能重启)
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we will be killed during cleaning up, bail.
return null;
}
// We'll just start a new process to host the content provider
//将运行状态标为false,使得重新启动ContentProvider所在进程
providerRunning = false;
conn = null;
dyingProc = cpr.proc;
} else {
cpr.proc.verifiedAdj = cpr.proc.setAdj;
}

Binder.restoreCallingIdentity(origId);
}
...
}
...
}

第二部分,如果目标ContentProvider正在运行中,首先检查目标ContentProvider是否可以在调用者进程中直接运行,需要满足以下任一一个条件:

  • 调用者和目标ContentProvider是同一个App中的同进程
  • 调用者和目标ContentProvider属同一个App且ContentProvider组件支持多进程(android:multiprocess

在这种情况下,直接返回一个新的ContentProviderHolder让调用者进程自己处理获得ContentProvider即可,具体逻辑在ActivityThread.installProvider方法中,后面会分析

如果不满足这种情况,即调用方进程和目标ContentProvider不在一个进程中,需要跨进程调用,获取ContentProviderConnection连接并更新引用计数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;

synchronized(this) {
...
//ContentProvider未在运行
if (!providerRunning) {
//通过PMS获取ContentProvider信息
try {
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
} catch (RemoteException ex) {
}
if (cpi == null) {
return null;
}
// If the provider is a singleton AND
// (it's a call within the same user || the provider is a
// privileged app)
// Then allow connecting to the singleton provider
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r == null ? callingUid : r.uid,
cpi.applicationInfo.uid);
if (singleton) {
userId = UserHandle.USER_SYSTEM;
}
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);

... //各项检查

ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
//通过Class(android:name属性)获取ContentProviderRecord
cpr = mProviderMap.getProviderByClass(comp, userId);
//此ContentProvider是第一次运行
boolean firstClass = cpr == null;
if (firstClass) {
final long ident = Binder.clearCallingIdentity();

... //权限处理

try {
//获取应用信息
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
if (ai == null) {
Slog.w(TAG, "No package info for content provider "
+ cpi.name);
return null;
}
ai = getAppInfoForUser(ai, userId);
//新建ContentProvider记录
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
} finally {
Binder.restoreCallingIdentity(ident);
}
} else if (dyingProc == cpr.proc && dyingProc != null) {
// The old stable connection's client should be killed during proc cleaning up,
// so do not re-use the old ContentProviderRecord, otherwise the new clients
// could get killed unexpectedly.
//旧的ContentProvider进程在死亡过程中,不要复用旧的ContentProviderRecord,避免出现预期之外的问题
cpr = new ContentProviderRecord(cpr);
// This is sort of "firstClass"
firstClass = true;
}

//如果此ContentProvider可以在调用者进程中直接运行(同一个App的同进程 或 同一个App且Provider组件支持多进程)
//直接返回一个新的ContentProviderHolder让调用者进程自己启动ContentProvider
if (r != null && cpr.canRunHere(r)) {
// If this is a multiprocess provider, then just return its
// info and allow the caller to instantiate it. Only do
// this if the provider is the same user as the caller's
// process, or can run as root (so can be in any process).
return cpr.newHolder(null);
}

// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
//查找正在启动中的ContentProvider
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}

// If the provider is not already being launched, then get it
// started.
//目标ContentProvider不在启动中
if (i >= N) {
final long origId = Binder.clearCallingIdentity();

try {
// Content provider is now in use, its package can't be stopped.
//将App状态置为unstopped,设置休眠状态为false
try {
AppGlobals.getPackageManager().setPackageStoppedState(
cpr.appInfo.packageName, false, userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ cpr.appInfo.packageName + ": " + e);
}

// Use existing process if already started
//获取目标ContentProvider所在进程记录
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) { //进程存活
if (!proc.pubProviders.containsKey(cpi.name)) {
//将ContentProviderRecord保存到进程已发布ContentProvider列表中
proc.pubProviders.put(cpi.name, cpr);
try {
//调度ActivityThread直接安装ContentProvider
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else { //进程死亡
//启动App(App启动过程中会自动启动ContentProvider)
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0,
new HostingRecord("content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name)),
ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
if (proc == null) {
...
return null;
}
}
cpr.launchingApp = proc;
//将目标ContentProvider添加到启动中列表
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}

// Make sure the provider is published (the same provider class
// may be published under multiple names).
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}

mProviderMap.putProviderByName(name, cpr);
//获取连接并更新引用计数
conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
stable);
if (conn != null) {
conn.waiting = true;
}
}

grantImplicitAccess(userId, null /*intent*/, callingUid,
UserHandle.getAppId(cpi.applicationInfo.uid));
}
...
}

第三部分,如果目标ContentProvider未在运行,先通过PMS获取ContentProvider信息,接着尝试通过Class(android:name属性)获取ContentProviderRecord,如果获取不到,说明这个ContentProvider是第一次运行(开机后),这种情况下需要新建ContentProviderRecord,如果获取到了,但是其所在进程被标记为正在死亡,此时同样需要新建ContentProviderRecord,不要复用旧的ContentProviderRecord,避免出现预期之外的问题

接下来同样检查目标ContentProvider是否可以在调用者进程中直接运行,如果可以直接返回一个新的ContentProviderHolder让调用者进程自己启动获取ContentProvider

接着检查正在启动中的ContentProvider列表,如果不在列表中,我们可能需要手动启动它,此时又有两种情况:

  1. ContentProvider所在进程已启动:如果进程已发布ContentProvider列表中不包含这个ContentProviderRecord,则将其添加到列表中,然后调用目标进程中的ApplicationThread.scheduleInstallProvider方法安装启动ContentProvider
  2. ContentProvider所在进程未启动:启动目标进程,目标进程启动过程中会自动安装启动ContentProviderActivityThread.handleBindApplication方法中)

最后更新mProviderMap,获取ContentProviderConnection连接并更新引用计数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;

// Wait for the provider to be published...
final long timeout =
SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;
boolean timedOut = false;
synchronized (cpr) {
while (cpr.provider == null) {
//ContentProvider启动过程中进程死亡,返回null
if (cpr.launchingApp == null) {
...
return null;
}
try {
//计算最大等待时间
final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
if (conn != null) {
conn.waiting = true;
}
//释放锁,等待ContentProvider启动完成
cpr.wait(wait);
//等待时间已过,ContentProvider还是没能启动完成并发布,超时
if (cpr.provider == null) {
timedOut = true;
break;
}
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
if (timedOut) {
... //超时处理
return null;
}

//返回新的ContentProviderHolder
return cpr.newHolder(conn);
}

第四部分,如果ContentProvider已存在,直接新建一个ContentProviderHolder返回,如果ContentProvider之前不存在,现在正在启动中,则以当前时间加上CONTENT_PROVIDER_READY_TIMEOUT_MILLIS推算出一个超时时间,给目标ContentProviderRecord上锁后,调用wait方法等待,直到ContentProvider成功发布后notify解除wait状态(在AMS.publishContentProviders方法中,之后会分析到),或一直等待直到超时。wait状态解除后,判断内部ContentProvider是否已被赋值,如果没有,则可以断定超时,此时返回null,如有,则返回一个新的ContentProviderHolder

ActivityThread.installProvider

由于这个方法同时包含了启动安装本地ContentProvider和获取安装远程ContentProvider的逻辑,所以放到后面启动ContentProvider章节里一起分析

启动ContentProvider

从前面的章节获取ContentProvider中,我们已经归纳出ContentProvider的启动分为两种情况,接着我们就来分析在这两种情况下,ContentProvider的启动路径

进程已启动

在进程已启动的情况下,如果进程已发布ContentProvider列表中不包含这个ContentProviderRecord,则将其添加到列表中,然后调用目标进程中的ApplicationThread.scheduleInstallProvider方法安装启动ContentProvider

ApplicationThread.scheduleInstallProvider会通过Hander发送一条what值为H.INSTALL_PROVIDER的消息,我们根据这个what值搜索,发现会走到ActivityThread.handleInstallProvider方法中,在这个方法内又会调用installContentProviders方法安装启动ContentProvider

进程未启动

在进程未启动的情况下,直接启动目标进程,在之前的文章 Android源码分析 - Activity启动流程(中) 里,我们分析了App的启动流程,其中有两个地方对启动ContentProvider至关重要

AMS.attachApplicationLocked

在这个方法中会调用generateApplicationProvidersLocked方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
...
//normalMode一般情况下均为true
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
...
}

private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
List<ProviderInfo> providers = null;
try {
//通过PMS获取App中同一个进程内的所有的ContentProvider组件信息
providers = AppGlobals.getPackageManager()
.queryContentProviders(app.processName, app.uid,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
| MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null)
.getList();
} catch (RemoteException ex) {
}
int userId = app.userId;
if (providers != null) {
int N = providers.size();
//有必要的情况下进行Map扩容
app.pubProviders.ensureCapacity(N + app.pubProviders.size());
for (int i=0; i<N; i++) {
// TODO: keep logic in sync with installEncryptionUnawareProviders
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
//对于单例ContentProvider,需要在默认用户中启动,如果不是默认用户的话则直接将其丢弃掉,不启动
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags);
if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) {
// This is a singleton provider, but a user besides the
// default user is asking to initialize a process it runs
// in... well, no, it doesn't actually run in this process,
// it runs in the process of the default user. Get rid of it.
providers.remove(i);
N--;
i--;
continue;
}

ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
if (cpr == null) {
//新建ContentProviderRecord并将其加入到缓存中
cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
mProviderMap.putProviderByClass(comp, cpr);
}
//将ContentProviderRecord保存到进程已发布ContentProvider列表中
app.pubProviders.put(cpi.name, cpr);
if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
//将App添加至进程中运行的包列表中
app.addPackage(cpi.applicationInfo.packageName,
cpi.applicationInfo.longVersionCode, mProcessStats);
}
notifyPackageUse(cpi.applicationInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
}
}
return providers;
}

这个方法主要是获取需要启动的ContentProviderContentProviderRecord,如果是第一次启动这个ContentProvider则需要新建一个ContentProviderRecord并将其存入缓存,然后将其保存到进程已发布ContentProvider列表中,以供后面使用。同时这个方法返回了需要启动的ProviderInfo列表,AMS.attachApplicationLocked方法可以根据这个列表判断是否有需要启动的ContentProvider并设置ContentProvider启动超时检测

ActivityThread.handleBindApplication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private void handleBindApplication(AppBindData data) {
...
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
//创建Application
app = data.info.makeApplication(data.restrictedBackupMode, null);

...

mInitialApplication = app;

// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
//在非受限模式下启动ContentProvider
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
}
}

...

//执行Application的onCreate方法
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
...
}
} finally {
// If the app targets < O-MR1, or doesn't change the thread policy
// during startup, clobber the policy to maintain behavior of b/36951662
if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1
|| StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
StrictMode.setThreadPolicy(savedPolicy);
}
}
...
}

可以看到,在这个方法中直接调用了installContentProviders方法安装启动ContentProvider

另外提一点,为什么我要把Application的创建和onCreate也放进来呢?现在市面上有很多库,包括很多教程告诉我们,可以通过注册ContentProvider的方式初始化SDK,获取全局Context,比如说著名的内存泄漏检测工具LeakCanary的新版本,想要使用它,直接添加它的依赖就行了,不需要对代码做哪怕一点的改动,究其原理,就是因为ContentProvider的启动时机是在Application创建后,Application.onCreate调用前,并且ContentProvider内的Context成员变量大概率就是Application,大家以后在开发过程中也可以妙用这一点

ActivityThread.installContentProviders

好了,现在这两种情况最终都走到了ActivityThread.installContentProviders方法中,那我们接下来就好好分析ContentProvider实际的启动安装流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();

for (ProviderInfo cpi : providers) {
//逐个启动
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}

try {
//发布ContentProvider
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}

这个方法很简单,便利所有待启动的ContentProvider信息列表,逐个启动安装ContentProvider,最后一起发布

ActivityThread.installProvider

我们先看installProvider方法,我们在上一章中分析到,获取ContentProvider的时候也会调用这个方法,这次我们就结合起来一起分析

通过上文的代码,我们发现,有两处地方会调用installProvider方法,方法的入参有三种形式,分别为:

  • holder不为nullinfo不为nullholder.providernull:在ActivityThread.acquireProvider方法中被调用,路径为 没有获取到已存在的ContentProvider -> AMS.getContentProvider -> AMS.getContentProviderImpl -> 发现目标ContentProvider可以在调用者进程中直接运行 -> 直接返回一个新的ContentProviderHolder(包含ProviderInfo) -> ActivityThread.installProvider,在这种情况下installProvider方法会在本地启动安装ContentProvider

  • holdernullinfo不为null:在ActivityThread.installContentProviders方法中被调用,两条路径,一是App进程启动后自动执行,二是在AMS.getContentProvider方法中发现目标进程已启动但是ContentProvider未启动,调用ActivityThread.scheduleInstallProvider方法执行,在这种情况下installProvider方法会在本地启动安装ContentProvider

  • holder不为nullholder.provider不为null:在ActivityThread.acquireProvider方法中被调用,路径为 没有获取到已存在的ContentProvider -> AMS.getContentProvider -> AMS.getContentProviderImpl -> 获取到目标进程的远程ContentProvider引用 -> 包装成ContentProviderHolder返回 -> ActivityThread.installProvider,在这种情况下installProvider方法直接可以获取到远程ContentProvider引用,然后进行处理

我们将这三种情况分成两种case分别分析

本地启动ContentProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) { //启动本地ContentProvider
Context c = null;
ApplicationInfo ai = info.applicationInfo;
//首先获取Context,一般情况下就是Application
if (context.getPackageName().equals(ai.packageName)) {
c = context;
} else if (mInitialApplication != null &&
mInitialApplication.getPackageName().equals(ai.packageName)) {
c = mInitialApplication;
} else {
try {
c = context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
}
if (c == null) {
return null;
}

//Split Apks动态加载相关
if (info.splitName != null) {
try {
c = c.createContextForSplit(info.splitName);
} catch (NameNotFoundException e) {
throw new RuntimeException(e);
}
}

try {
final java.lang.ClassLoader cl = c.getClassLoader();
//获取应用信息
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
//通过AppComponentFactory实例化ContentProvider
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
//Transport类,继承自ContentProviderNative(Binder服务端)
provider = localProvider.getIContentProvider();
if (provider == null) {
return null;
}
// XXX Need to create the correct context for this provider.
//初始化ContentProvider,调用其onCreate方法
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
if (!mInstrumentation.onException(null, e)) {
throw new RuntimeException(
"Unable to get provider " + info.name
+ ": " + e.toString(), e);
}
return null;
}
} else { //获取外部ContentProvider
...
}

ContentProviderHolder retHolder;

synchronized (mProviderMap) {
//对于本地ContentProvider来说,这里的实际类型是Transport,继承自ContentProviderNative(Binder服务端)
IBinder jBinder = provider.asBinder();
if (localProvider != null) { //本地启动ContentProvider的情况
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
//如果已经存在相应的ContentProvider记录,使用其内部已存在的ContentProvider
provider = pr.mProvider;
} else {
//否则使用新创建的ContentProvider
holder = new ContentProviderHolder(info);
holder.provider = provider;
//对于本地ContentProvider来说,不存在释放引用这种情况
holder.noReleaseNeeded = true;
//创建ProviderClientRecord并将其保存到mProviderMap本地缓存中
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
//保存ProviderClientRecord到本地缓存中
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
} else { //获取远程ContentProvider的情况
...
}
}
return retHolder;
}

我们在这里找到了ContentProvider创建并启动的入口,首先通过传入的Context(实际上就是Application)判断并确定创建并给ContentProvider使用的的Context是什么(一般情况下也是Application),然后获取到应用信息LoadedApk,再通过它得到AppComponentFactory(前面的文章中介绍过,如果没有在AndroidManifest中设置android:appComponentFactory属性,使用的便是默认的AppComponentFactory),接着通过AppComponentFactory.instantiateProvider方法实例化ContentProvider对象

1
2
3
4
5
public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,
@NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (ContentProvider) cl.loadClass(className).newInstance();
}

默认的话就是通过ClassName反射调用默认构造函数实例化ContentProvider对象,最后再调用ContentProvider.attachInfo方法初始化

1
2
3
4
5
6
7
8
9
10
11
12
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
...
if (mContext == null) {
mContext = context;
...
ContentProvider.this.onCreate();
}
}

详细内容我们就不分析了,只需要知道这里给mContext赋了值,然后调用了ContentProvider.onCreate方法就可以了

到了这一步,ContentProvider就算是启动完成了,接下来需要执行一些安装步骤,其实也就是对缓存等进行一些处理。在ContentProvider实例化后,会调用其getIContentProvider方法给provider变量赋值,这里获得的对象其实是一个Transport对象,继承自ContentProviderNative,是一个Binder服务端对象,在ContentProvider初始化后,会对Transport对象调用asBinder方法获得Binder对象,这里获得的其实还是自己本身,接着从缓存中尝试获取ProviderClientRecord对象,如果获取到了,说明已经存在了相应的ContentProvider,使用ProviderClientRecord内部的ContentProvider,刚刚新创建的那个就可以丢弃了,如果没获取到,就去新建ContentProviderHolder以及ProviderClientRecord,然后将他们添加到各种缓存中,至此,ContentProvider的安装过程也到此结束

获取处理远程ContentProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) { //启动本地ContentProvider
...
} else { //获取外部ContentProvider
//实际类型为ContentProviderProxy
provider = holder.provider;
}

ContentProviderHolder retHolder;

synchronized (mProviderMap) {
//对于外部ContentProvider来说,这里的实际类型是BinderProxy
IBinder jBinder = provider.asBinder();
if (localProvider != null) { //本地启动ContentProvider的情况
...
} else { //获取远程ContentProvider的情况
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) { //如果ContentProvider引用已存在
// We need to transfer our new reference to the existing
// ref count, releasing the old one... but only if
// release is needed (that is, it is not running in the
// system process).
//对于远程ContentProvider来说,如果目标App为system应用(UID为ROOT_UID或SYSTEM_UID)
//并且目标App不为设置(包名不为com.android.settings),则noReleaseNeeded为true
if (!noReleaseNeeded) {
//增加已存在的ContentProvider引用的引用计数
incProviderRefLocked(prc, stable);
try {
//释放传入的引用,移除ContentProviderConnection相关信息,更新引用计数
ActivityManager.getService().removeContentProvider(
holder.connection, stable);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
//创建ProviderClientRecord并将其保存到mProviderMap本地缓存中
ProviderClientRecord client = installProviderAuthoritiesLocked(
provider, localProvider, holder);
if (noReleaseNeeded) { //同上,目标App为system应用,不需要释放引用
//新建一个ProviderRefCount,但引用计数初始化为一个较大的数值
//这样后续无论调用方进程的ContentProvider引用计数如何变动都不会影响到AMS
prc = new ProviderRefCount(holder, client, 1000, 1000);
} else { //需要释放引用的情况下
//正常的新建初始化一个ProviderRefCount
prc = stable
? new ProviderRefCount(holder, client, 1, 0)
: new ProviderRefCount(holder, client, 0, 1);
}
//保存至缓存
mProviderRefCountMap.put(jBinder, prc);
}
retHolder = prc.holder;
}
}
return retHolder;
}

对于holder.provider不为null的情况,直接获取远程ContentProvider引用,然后进行处理就可以了。这里获取到的IContentProvider的实际类型是ContentProviderProxy,然后对其调用asBinder方法,获取到的是BinderProxy对象,接着从缓存中尝试获取ProviderRefCount对象,如果缓存中已经有相应的引用对象了,则在需要释放引用(!noReleaseNeeded)的情况下使用原有的引用,释放参数传入进来的ContentProvider引用

这里noReleaseNeeded是在ContentProviderRecord构造时赋值的,为true的条件是目标App为system应用(UIDROOT_UIDSYSTEM_UID)并且目标App不为设置(包名不为com.android.settings

如果缓存中没有查找到相应的ProviderRefCount对象,新建ProviderClientRecordProviderRefCount对象,并将他们保存到缓存中,至于为什么在noReleaseNeeded的情况下,新建的ProviderRefCount的引用计数初始值为1000,我猜测是因为noReleaseNeeded代表了不需要释放引用,所以这里干脆设置一个比较大的值,这样无论调用方进程的ContentProvider引用计数怎样变动,都不会再调用到AMS的方法中去处理引用的变化,在非常早期的Android版本中(Android 4.0.1),这个值曾被设置为10000

至此,远程ContentProvider的安装也结束了

ActivityThread.installProviderAuthoritiesLocked

接下来我们再简单的看一下两种case都会走到的installProviderAuthoritiesLocked方法吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
ContentProvider localProvider, ContentProviderHolder holder) {
final String auths[] = holder.info.authority.split(";");
final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
...
final ProviderClientRecord pcr = new ProviderClientRecord(
auths, provider, localProvider, holder);
for (String auth : auths) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord existing = mProviderMap.get(key);
if (existing != null) {
Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
+ " already published as " + auth);
} else {
mProviderMap.put(key, pcr);
}
}
return pcr;
}

这个方法很简单,新建了一个ProviderClientRecord并将其添加到mProviderMap缓存中,这里的操作对应着最前面的acquireExistingProvider方法,有了这个缓存,以后就可以直接拿,而不用再复杂的经过一系列的AMS跨进程操作了

AMS.publishContentProviders

ContentProvider全部启动安装完后,便要调用AMS.publishContentProviders将他们发布出去,供外部使用了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}

synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when publishing content providers");
}

final long origId = Binder.clearCallingIdentity();

final int N = providers.size();
for (int i = 0; i < N; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
//App进程启动时或AMS.getContentProvider中已经将相应ContentProviderRecord添加到了pubProviders中
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
//保存至缓存中
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}

//ContentProvider已经启动完毕,将其从正在启动的ContentProvider列表中移除
int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
//移除ContentProvider启动超时监听
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
// Make sure the package is associated with the process.
// XXX We shouldn't need to do this, since we have added the package
// when we generated the providers in generateApplicationProvidersLocked().
// But for some reason in some cases we get here with the package no longer
// added... for now just patch it in to make things happy.
r.addPackage(dst.info.applicationInfo.packageName,
dst.info.applicationInfo.longVersionCode, mProcessStats);
synchronized (dst) {
dst.provider = src.provider;
dst.setProcess(r);
//让出锁,通知其他wait的地方
//对应着AMS.getContentProvider的第四部分:等待ContentProvider启动完成
dst.notifyAll();
}
dst.mRestartCount = 0;
updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
}

Binder.restoreCallingIdentity(origId);
}
}

遍历整个待发布的ContentProvider列表,从ProcessRecord.pubProviders中查找相对应的ContentProviderRecord,我们在之前的章节中已经分析过了,App进程启动时或AMS.getContentProvider中已经将相应ContentProviderRecord添加到了pubProviders中,然后就是将其保存到各个缓存中,由于ContentProvider已经启动完毕,所以需要将其从正在启动的ContentProvider列表中移除,在ContentProvider正常启动的情况下,我们需要将ContentProvider的启动超时监听移除,最后,获取ContentProviderRecord同步锁,将准备好的ContentProvider赋值到ContentProviderRecord中,接着调用notifyAll方法通知其他调用过wait的地方,将锁让出,这里对应的就是AMS.getContentProvider的第四部分:等待ContentProvider启动完成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
...
//这里的cpr和在publishContentProviders获得的dst是一个对象
synchronized (cpr) {
while (cpr.provider == null) {
...
//释放锁,等待ContentProvider启动完成
cpr.wait(wait);
...
}
}
...
}

这样,ContentProvider一发布,这里就会收到通知,解除wait状态,获得到ContentProvider,返回出去,是不是感觉一切都串起来了?

ContentProvider引用计数

ContentProvider的获取与启动分析完了,接下来我们聊聊它的引用计数,为下一小节分析ContentProvider死亡杀死调用方进程的过程做准备

ActivityThread层的引用计数是和AMS层的引用计数分开的,ActivityThread记录的是目标ContentProvider在本进程中有多少处正在使用,而AMS记录的是目标ContentProvider正在被多少个进程使用

ActivityThread层的引用计数

增加引用计数

我们先从ActivityThread层增加引用计数开始说起,在ActivityThread获取ContentProvider时,便会调用incProviderRefLocked方法来增加引用计数,具体的时机为acquireExistingProviderinstallProvider时,代码我就不重复放了,大家看前面几个小节就行(后同)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
private final void incProviderRefLocked(ProviderRefCount prc, boolean stable) {
if (stable) {
//增加ActivityThread的stable引用计数
prc.stableCount += 1;
//本进程对目标ContentProvider产生了stable引用关系
if (prc.stableCount == 1) {
// We are acquiring a new stable reference on the provider.
int unstableDelta;
//正在移除ContentProvider引用中(释放ContentProvider后发现stable和unstable引用计数均为0)
if (prc.removePending) {
// We have a pending remove operation, which is holding the
// last unstable reference. At this point we are converting
// that unstable reference to our new stable reference.
//当ActivityThread释放一个stable的ContentProvider时,如果释放完后,
//发现stable和unstable引用计数均为0,则会暂时保留一个unstable引用
//所以这里需要为 -1 ,将这个unstable引用移除
unstableDelta = -1;
// Cancel the removal of the provider.
prc.removePending = false;
// There is a race! It fails to remove the message, which
// will be handled in completeRemoveProvider().
//取消移除ContentProvider引用
mH.removeMessages(H.REMOVE_PROVIDER, prc);
} else {
//对于正常情况,只需要增加stable引用计数,不需要动unstable引用计数
unstableDelta = 0;
}
try {
//AMS层修改引用计数
ActivityManager.getService().refContentProvider(
prc.holder.connection, 1, unstableDelta);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
//增加ActivityThread的unstable引用计数
prc.unstableCount += 1;
//本进程对目标ContentProvider产生了unstable引用关系
if (prc.unstableCount == 1) {
// We are acquiring a new unstable reference on the provider.
//正在移除ContentProvider引用中(释放ContentProvider后发现stable和unstable引用计数均为0)
if (prc.removePending) {
// Oh look, we actually have a remove pending for the
// provider, which is still holding the last unstable
// reference. We just need to cancel that to take new
// ownership of the reference.
//取消移除ContentProvider引用
prc.removePending = false;
mH.removeMessages(H.REMOVE_PROVIDER, prc);
} else {
// First unstable ref, increment our count in the
// activity manager.
try {
//增加AMS层的unstable引用计数
ActivityManager.getService().refContentProvider(
prc.holder.connection, 0, 1);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
}
}
}

这里的逻辑需要配合着ContentProvider释放引用那里一起看才好理解,我先提前解释一下

首先,removePending这个变量表示此ContentProvider正在移除中,当ActivityThread减少引用计数,检查到stableunstable引用计数均为0后被赋值为true,并且会向Handler发送一条what值为REMOVE_PROVIDER的延时消息,在一定时间后便会触发ContentProvider移除操作,清理本地缓存,再将removePending重新置为false,所以当这里removePendingtrue则说明此ContentProvider还没完全被移除,我们把这个消息取消掉继续使用这个ContentProvider

对于stable引用的情况下,当ActivityThread减少引用计数,检查到stableunstable引用计数均为0后,会暂时保留一个unstable引用,等到后面真正触发到了移除ContentProvider的时候再将这个unstable引用移除,所以在增加引用计数的时候需要考虑到这一点,在这种情况下要将AMS层的unstable引用计数减一

对于其他的情况就是正常的增加ActivityThread层引用计数,然后调用AMS.refContentProvider方法操作AMS层的引用计数

减少引用计数

ContentProvider使用完后会调用ActivityThread.releaseProvider方法,以query方法为例,最后会调用releaseUnstableProviderreleaseProvider方法,最终都会走到这里来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public final boolean releaseProvider(IContentProvider provider, boolean stable) {
if (provider == null) {
return false;
}

IBinder jBinder = provider.asBinder();
synchronized (mProviderMap) {
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc == null) {
// The provider has no ref count, no release is needed.
return false;
}

boolean lastRef = false;
if (stable) {
//引用计数已经为0,无法再减了
if (prc.stableCount == 0) {
return false;
}
//减少ActivityThread的stable引用计数
prc.stableCount -= 1;
if (prc.stableCount == 0) {
// What we do at this point depends on whether there are
// any unstable refs left: if there are, we just tell the
// activity manager to decrement its stable count; if there
// aren't, we need to enqueue this provider to be removed,
// and convert to holding a single unstable ref while
// doing so.
lastRef = prc.unstableCount == 0;
try {
//如果是最后的引用,则暂时保留一个unstable引用
ActivityManager.getService().refContentProvider(
prc.holder.connection, -1, lastRef ? 1 : 0);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
//引用计数已经为0,无法再减了
if (prc.unstableCount == 0) {
return false;
}
//减少ActivityThread的unstable引用计数
prc.unstableCount -= 1;
if (prc.unstableCount == 0) {
// If this is the last reference, we need to enqueue
// this provider to be removed instead of telling the
// activity manager to remove it at this point.
lastRef = prc.stableCount == 0;
//如果是最后的引用,则不进入到这里,暂时保留一个unstable引用
if (!lastRef) {
try {
//减少AMS引用计数
ActivityManager.getService().refContentProvider(
prc.holder.connection, 0, -1);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
}
}

if (lastRef) {
if (!prc.removePending) {
// Schedule the actual remove asynchronously, since we don't know the context
// this will be called in.
//表面此ContentProvider正在移除中
prc.removePending = true;
//发送延时消息,等待一定时间后移除ContentProvider
Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, prc);
mH.sendMessageDelayed(msg, CONTENT_PROVIDER_RETAIN_TIME);
} else {
Slog.w(TAG, "Duplicate remove pending of provider " + prc.holder.info.name);
}
}
return true;
}
}

可以看到,在减完引用计数后,如果发现是最后一个引用,即stableunstable引用计数均为0,此时无论是stable还是unstable都会让AMS暂时保留一个unstable引用,然后发送一条what值为REMOVE_PROVIDER的延时消息,等待一定时间后移除ContentProvider,当时间到了触发这条消息时,会调用到ActivityThread.completeRemoveProvider方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
final void completeRemoveProvider(ProviderRefCount prc) {
synchronized (mProviderMap) {
if (!prc.removePending) {
// There was a race! Some other client managed to acquire
// the provider before the removal was completed.
// Abort the removal. We will do it later.
return;
}

// More complicated race!! Some client managed to acquire the
// provider and release it before the removal was completed.
// Continue the removal, and abort the next remove message.
prc.removePending = false;

//移除缓存
final IBinder jBinder = prc.holder.provider.asBinder();
ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder);
if (existingPrc == prc) {
mProviderRefCountMap.remove(jBinder);
}

//移除缓存
for (int i=mProviderMap.size()-1; i>=0; i--) {
ProviderClientRecord pr = mProviderMap.valueAt(i);
IBinder myBinder = pr.mProvider.asBinder();
if (myBinder == jBinder) {
mProviderMap.removeAt(i);
}
}
}

try {
//处理AMS层引用计数
ActivityManager.getService().removeContentProvider(
prc.holder.connection, false);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}

这个方法将进程内所持有的ContentProvider相关缓存清除,然后调用AMS.removeContentProvider方法通知AMS移除ContentProvider,处理相应的引用计数。这里我们发现,调用AMS.removeContentProvider方法传入的最后一个参数stablefalse,因为我们之前在stableunstable引用计数均为0的情况下,保留了一个unstable引用,所以这时移除的ContentProvider引用也是unstable引用

AMS层的引用计数

接着我们来看AMS层的引用计数

AMS.refContentProvider

我们就先从我们刚刚分析的ActivityThread层的引用计数修改后续:refContentProvider 看起

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public boolean refContentProvider(IBinder connection, int stable, int unstable) {
ContentProviderConnection conn;
...
conn = (ContentProviderConnection)connection;
...

synchronized (this) {
if (stable > 0) {
conn.numStableIncs += stable;
}
stable = conn.stableCount + stable;
if (stable < 0) {
throw new IllegalStateException("stableCount < 0: " + stable);
}

if (unstable > 0) {
conn.numUnstableIncs += unstable;
}
unstable = conn.unstableCount + unstable;
if (unstable < 0) {
throw new IllegalStateException("unstableCount < 0: " + unstable);
}

if ((stable+unstable) <= 0) {
throw new IllegalStateException("ref counts can't go to zero here: stable="
+ stable + " unstable=" + unstable);
}
conn.stableCount = stable;
conn.unstableCount = unstable;
return !conn.dead;
}
}

这个方法很简单,应该不需要再多做分析了吧?就是简单的修改ContentProviderConnection的引用计数值

AMS.incProviderCountLocked

接下来我们看AMS层引用计数的增加,AMS.incProviderCountLocked这个方法的触发时机是在AMS.getContentProviderImpl方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
ContentProviderConnection incProviderCountLocked(ProcessRecord r,
final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
String callingPackage, String callingTag, boolean stable) {
if (r != null) {
for (int i=0; i<r.conProviders.size(); i++) {
ContentProviderConnection conn = r.conProviders.get(i);
//如果连接已存在,在其基础上增加引用计数
if (conn.provider == cpr) {
if (stable) {
conn.stableCount++;
conn.numStableIncs++;
} else {
conn.unstableCount++;
conn.numUnstableIncs++;
}
return conn;
}
}
//新建ContentProviderConnection连接
ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
//建立关联
conn.startAssociationIfNeeded();
if (stable) {
conn.stableCount = 1;
conn.numStableIncs = 1;
} else {
conn.unstableCount = 1;
conn.numUnstableIncs = 1;
}
//添加连接
cpr.connections.add(conn);
r.conProviders.add(conn);
//建立关联
startAssociationLocked(r.uid, r.processName, r.getCurProcState(),
cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
return conn;
}
cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
return null;
}

如果调用方进程已存在对应ContentProviderConnection连接,则在其基础上增加引用计数,否则新建连接,然后初始化引用计数值

AMS.decProviderCountLocked

然后是减少引用计数,之前在ActivityThread减引用到0后,会延时调用ActivityThread.completeRemoveProvider方法,在这个方法中会调用到AMS.removeContentProvider方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void removeContentProvider(IBinder connection, boolean stable) {
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
ContentProviderConnection conn = (ContentProviderConnection)connection;
...
//减少引用计数
if (decProviderCountLocked(conn, null, null, stable)) {
//更新进程优先级
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}

在这个方法中便会调用AMS.decProviderCountLocked减少引用计数,然后更新进程优先级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
boolean decProviderCountLocked(ContentProviderConnection conn,
ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
if (conn != null) {
cpr = conn.provider;
//减少引用计数值
if (stable) {
conn.stableCount--;
} else {
conn.unstableCount--;
}
if (conn.stableCount == 0 && conn.unstableCount == 0) {
//停止关联
conn.stopAssociation();
//移除连接
cpr.connections.remove(conn);
conn.client.conProviders.remove(conn);
if (conn.client.setProcState < PROCESS_STATE_LAST_ACTIVITY) {
// The client is more important than last activity -- note the time this
// is happening, so we keep the old provider process around a bit as last
// activity to avoid thrashing it.
if (cpr.proc != null) {
cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
}
}
//停止关联
stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
return true;
}
return false;
}
cpr.removeExternalProcessHandleLocked(externalProcessToken);
return false;
}

减少引用计数值,如果stableunstable引用计数均为0,则将这个连接移除

ContentProvider死亡杀死调用方进程的过程

我们前面提到过,ContentProvider所在进程死亡会将与其所有有stable关联的调用方进程杀死,这是怎么做到的呢?在之前的文章中,我们介绍过进程启动时,在调用AMS.attachApplicationLocked时会注册一个App进程死亡回调,我们就从进程死亡,触发死亡回调开始分析

1
2
3
4
5
6
7
8
9
10
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
...
//注册App进程死亡回调
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
...
}

注册了死亡回调后,如果对应binder进程死亡,便会回调IBinder.DeathRecipient.binderDied方法,我们来看一下AppDeathRecipient对这个方法的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;

AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread) {
mApp = app;
mPid = pid;
mAppThread = thread;
}

@Override
public void binderDied() {
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread, true, null);
}
}
}

直接转手调用AMS.appDiedLocked方法,然后经过handleAppDiedLocked调用到cleanUpApplicationRecordLocked方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
...

boolean restart = false;

// Remove published content providers.
//清除已发布的ContentProvider
for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
ContentProviderRecord cpr = app.pubProviders.valueAt(i);
if (cpr.proc != app) {
// If the hosting process record isn't really us, bail out
continue;
}
final boolean alwaysRemove = app.bad || !allowRestart;
final boolean inLaunching = removeDyingProviderLocked(app, cpr, alwaysRemove);
if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {
// We left the provider in the launching list, need to
// restart it.
restart = true;
}

cpr.provider = null;
cpr.setProcess(null);
}
app.pubProviders.clear();

// Take care of any launching providers waiting for this process.
//清除正在启动中的ContentProvider
if (cleanupAppInLaunchingProvidersLocked(app, false)) {
mProcessList.noteProcessDiedLocked(app);
restart = true;
}

// Unregister from connected content providers.
//清除已连接的ContentProvider
if (!app.conProviders.isEmpty()) {
for (int i = app.conProviders.size() - 1; i >= 0; i--) {
ContentProviderConnection conn = app.conProviders.get(i);
conn.provider.connections.remove(conn);
stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
conn.provider.appInfo.longVersionCode, conn.provider.name,
conn.provider.info.processName);
}
app.conProviders.clear();
}
...
}

可以看到,这个方法中遍历了ProcessRecord.pubProviders,逐个对发布的ContentProvider调用removeDyingProviderLocked方法执行移除操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
private final boolean removeDyingProviderLocked(ProcessRecord proc,
ContentProviderRecord cpr, boolean always) {
boolean inLaunching = mLaunchingProviders.contains(cpr);
if (inLaunching && !always && ++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
// It's being launched but we've reached maximum attempts, force the removal
always = true;
}

if (!inLaunching || always) {
synchronized (cpr) {
cpr.launchingApp = null;
cpr.notifyAll();
}
final int userId = UserHandle.getUserId(cpr.uid);
// Don't remove from provider map if it doesn't match
// could be a new content provider is starting
//移除缓存
if (mProviderMap.getProviderByClass(cpr.name, userId) == cpr) {
mProviderMap.removeProviderByClass(cpr.name, userId);
}
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
// Don't remove from provider map if it doesn't match
// could be a new content provider is starting
//移除缓存
if (mProviderMap.getProviderByName(names[j], userId) == cpr) {
mProviderMap.removeProviderByName(names[j], userId);
}
}
}

for (int i = cpr.connections.size() - 1; i >= 0; i--) {
ContentProviderConnection conn = cpr.connections.get(i);
if (conn.waiting) {
// If this connection is waiting for the provider, then we don't
// need to mess with its process unless we are always removing
// or for some reason the provider is not currently launching.
if (inLaunching && !always) {
continue;
}
}
ProcessRecord capp = conn.client;
conn.dead = true;
if (conn.stableCount > 0) {
if (!capp.isPersistent() && capp.thread != null
&& capp.pid != 0
&& capp.pid != MY_PID) {
//当调用方与被杀死的目标ContentProvider进程间有stable连接
//并且调用方App进程非persistent进程并且非system_server进程中的情况下
//杀死调用方进程
capp.kill("depends on provider "
+ cpr.name.flattenToShortString()
+ " in dying proc " + (proc != null ? proc.processName : "??")
+ " (adj " + (proc != null ? proc.setAdj : "??") + ")",
ApplicationExitInfo.REASON_DEPENDENCY_DIED,
ApplicationExitInfo.SUBREASON_UNKNOWN,
true);
}
} else if (capp.thread != null && conn.provider.provider != null) {
try {
//通知调用方移除ContentProvider
capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
} catch (RemoteException e) {
}
// In the protocol here, we don't expect the client to correctly
// clean up this connection, we'll just remove it.
//移除连接
cpr.connections.remove(i);
if (conn.client.conProviders.remove(conn)) {
stopAssociationLocked(capp.uid, capp.processName, cpr.uid,
cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
}
}
}

if (inLaunching && always) {
mLaunchingProviders.remove(cpr);
cpr.mRestartCount = 0;
inLaunching = false;
}
return inLaunching;
}

可以看到,在这个方法中遍历了ContentProvider下的所有连接,当发现有其他进程与自己建立了stable连接(conn.stableCount > 0),且调用方进程不是persistent进程(常驻进程,只有拥有系统签名的App设置这个属性才生效),也不是运行在system_server进程,调用ProcessRecord.kill方法直接杀死进程,对于没有建立stable连接的调用方进程,调用IApplicationThread.unstableProviderDied方法通知调用方进程移除相应的ContentProvider

所以,使用ContentProvider是有一定风险的,大家要注意规避

总结

到这里,整个Framework层关于ContentProvider的内容应该都分析完了,希望大家看完后能获得一些收获,接下来的文章应该会去分析Service相关源码,敬请期待~


有手就行!Sovits AI人声模型训练

简介

随着AI孙燕姿的爆火出圈,各大视频平台的AI人声投稿量激增,B站首页频频给我推送相关视频,正好我对AIGC方面也挺感兴趣,心动不如行动,于是我用自己的声音训练了一个模型,发现整个过程异常的简单,并且最终出来的效果也不错,真的是有手就行,所以这次将我自己训练推理的过程经验分享给大家

Sovits

首先介绍一下今天要讲的Sovits,So-vits-svc(也称Sovits)是由是中国民间歌声合成爱好者Rcell基于VITS、soft-vc、VISinger2等一系列项目开发的一款开源免费AI语音转换软件,通过SoftVC内容编码器提取源音频语音特征,与F0同时输入VITS替换原本的文本输入达到歌声转换的效果。

由于某些原因,原作者Rcell删除了原代码仓库,现由svc-develop-team接手进行后续维护,现仓库地址:https://github.com/svc-develop-team/so-vits-svc

配置要求

  • 一张支持 CUDA 的,拥有至少 6G 以上显存的 NVIDIA 显卡
  • 推荐使用Windows系统,教程后续的素材处理、训练、推理均在Windows平台上完成,同时使用整合包GUI也可以帮助新手将注意力集中在训练/推理本身上,避免了繁杂的环境配置等工作

环境配置

本次的环境使用的是 bilibili@羽毛布団 大佬提供的整合包:https://www.yuque.com/umoubuton/ueupp5/sdahi7m5m6r0ur1r ,在这里能找到下载地址和一些说明

下载完后,你会得到一个Sovits的压缩包和一些工具软件,将其中的so-vits-svc解压缩后,打开里面的启动webui.bat文件,它会自动准备环境,然后弹出一个网页

sovits目录

启动webui

在网页中将Tab页切换至训练页,可以看到我们的显卡信息

显卡信息

确认无误后我们就可以开始训练了

准备数据集

数据集的质量及多少决定了训练模型质量的上下限,质量差的数据集无论你训练多久,训练了多少万个step,都不可能达到一个理想的效果

准备干声

Sovits的训练我们需要找一些想训练的声线所对应的干声素材,不可混杂多种声线,时长最好在2个小时以上,最低不要少于30分钟,否则无法保证训练出来的模型的质量,可以是说话、读书或唱歌的声音(有的人说加入一些哭闹、大笑等各种各样的声音有奇效,我没尝试过,大家可以试试看),如果想让你的模型唱歌的话,唱歌的素材需要覆盖低中高音多个频率

干声素材中不能有伴奏、混响、和声(避免转换后的声音自带BGM),不要有换气声、颤音、转音等,尽量将背景噪音去除干净

降噪

如果你本来的素材就是不带伴奏混响的干声文件,但是有一些嘈杂的背景噪音,可以使用Adobe Audition(以下简称AU)进行降噪处理

教程:https://helpx.adobe.com/cn/audition/using/noise-reduction-restoration-effects.html

去伴奏混响

如果你的素材是之前录好的歌曲,那么需要对它进行去伴奏去混响处理,这里推荐使用 Ultimate Vocal Remover v5(简称UVR5)

下载完UVR5,在处理音频前建议先去Advanced VR Options中将Post-Process选项打开,这样去混响的效果可能更好(玄学)

UVR5 Settings

UVR5 Advanced VR Options

接着开始去伴奏,推荐采用以下配置:

  1. Process Method: Demucs
  2. Stems: Vocals
  3. Demucs Model: v3 | UVR_Model_1 注:找不到对应模型的可以在下拉框中选择Download在软件内下载相应模型
  4. 勾选GPU Conversion

UVR5去伴奏

然后对处理完后的人声文件再做一次去混响处理,推荐采用以下配置:

  1. Process Method: VR Architecture
  2. Window Size: 320
  3. Aggression Setting: 10
  4. VR Model: 5_HP-Karaoke_UVR
  5. 勾选GPU Conversion
  6. 勾选Voacls Only

UVR5去混响

这样简单的几步,我们就将干声从歌曲文件中提取出来了,如果你觉得提取出来的效果差强人意,也可以使用 RipX DeepAudio 做一些精修

将文件导入RipX中,会产生如下图一样的一段曲线

点击其中的某段黄色曲线可以播放这段音频,我们可以一段段听过去,遇到杂音部分可以将其手动删除

RipX

匹配响度

如果你的干声素材的来源不同,很可能会有素材响度大小不一的情况,这种情况下需要使用AU对所有音频去做匹配响度,使所有素材的分贝值在一个统一的范围内

教程:https://helpx.adobe.com/cn/audition/using/match-loudness.html

压限

干声的响度不宜超过-6db,建议使用Adobe Audition中的压限器将声音的分贝限制在-6db以内

教程:https://helpx.adobe.com/cn/audition/using/amplitude-compression-effects.html

切片

干声素材都处理完后,下一步就是将这些素材切成一个个2-15s的小片段(片段太长容易爆显存,最好不要超过20s),这里推荐使用 Audio Slicer ,可以根据响度阈值和间隔时间等自动将音频切片

Audio Slicer

将干声文件拖进去后可以先使用默认参数切一遍,然后去输出目录,将文件按文件大小由大到小排列,查看是否还有大于15s的音频切片,如有的话,将这些音频重新拖入软件中,按照以下参数重新切片一次:

  1. Threshold(db): -20
  2. Minimum Interval: 100
  3. Maximum Silence Length(ms): 500

一般这样就不会有大于15s的音频了,如果还有的话,我将这些参数的含义列在下面,大家可以自己调整参数尝试,大家也可以使用其他的一些音频处理工具(如:AU)手动切片

  • Threshold(阈值)

以 dB 表示的 RMS 阈值。所有 RMS 值都低于此阈值的区域将被视为静音。如果音频有噪音,请增加此值。默认值为 -40。

  • Minimum Length(最小长度)

每个切片音频剪辑所需的最小长度,以毫秒为单位。默认值为 5000。

  • Minimum Interval(最小间距)

要切片的静音部分的最小长度,以毫秒为单位。如果音频仅包含短暂的中断,请将此值设置得更小。此值越小,此应用程序可能生成的切片音频剪辑就越多。请注意,此值必须小于 min length 且大于 hop size。默认值为 300。

  • Hop Size(跳跃步长)

每个 RMS 帧的长度,以毫秒为单位。增加此值将提高切片的精度,但会降低处理速度。默认值为 10。

  • Maximum Silence Length(最大静音长度)

在切片音频周围保持的最大静音长度,以毫秒为单位。根据需要调整此值。请注意,设置此值并不意味着切片音频中的静音部分具有完全给定的长度。如上所述,该算法将搜索要切片的最佳位置。默认值为 1000。

最后,记得把过短的音频切片删除掉

格式转换

数据集的格式必须是wav原始波形格式,大家检查一下,如果如果有非wav格式的文件,需要做一下格式转换,推荐使用Foobar2000,或者直接使用FFmepg进行转换

重命名

数据集中不能出现中文等非英文字符,特殊字符推荐只使用下划线,Windows下直接将文件全选后重命名,文件名会变成以下这种格式:

  • xxx (1).wav
  • xxx (2).wav

本人亲测这样命名虽然会报Warning,但不影响训练,是可行的

预训练

接着我们就开始准备训练了

放置数据集

首先将处理好的干声全部放到一个文件夹下,然后再将这个文件夹放到so-vits-svc目录下的dataset_raw文件夹下

放置数据集

数据预处理

打开webui界面,切换到训练选项卡,首先点击识别数据集,上面的文本框中便会显示出我们准备好的数据集名,然后选择训练使用的编码器f0预测器,这里选择我图中标出的两个选项,是目前效果比较好的选项,接着点击数据预处理,在预处理输出信息那一栏会打印进度,耐心等待它跑完

数据预处理

训练配置

等待数据预处理完成后,我们要将训练的设置和参数写入到配置文件中

训练配置

介绍一下这里参数的含义和推荐设置:

  1. 每隔多少步(steps)生成一次评估日志:每隔一定步数输出一下当前步数下的学习率,loss值等信息,根据个人偏好自己填写即可
  2. 每隔多少步(steps)验证并保存一次模型:字面意思,根据个人偏好自己填写即可
  3. 仅保留最新的X个模型:我训练到11万步时一个模型接近600MB,大家根据自己的硬盘大小和个人偏好填写即可
  4. 批量大小:大的batch size可以减少训练时间,提高稳定性,但同时也会导致模型泛化能力下降,所以,就算你的显存很大也不建议将本参数设置的过大,推荐使用4
  5. 学习率:初始学习率过大会导致模型无法收敛,过小则会导致模型收敛特别慢或无法学习,建议使用默认值0.0001
  6. 使用fp16混合精度训练:混合精度训练是在尽可能减少精度损失的情况下利用半精度浮点数加速训练,它使用FP16即半精度浮点数存储权重和梯度,在减少占用内存的同时起到了加速训练的效果,理论上来说使用混合精度几乎不会造成精度损失,但目前没对模型质量的影响尚未查证,在显卡性能足够的情况下建议还是先不要勾选
  7. 加载数据集到内存中:在内存足够的情况下建议勾选,可以加快训练速度

所有训练参数设置好后,点击下面的写入配置文件按钮,在下面的输出信息那里会显示配置文件写入完成,接下来就可以开始正式训练了

写入配置文件

正式训练

点击下面的从头开始训练,会弹出一个新的终端窗口,在这个终端窗口中,会不断的输出当前训练的日志

开始训练

训练终端窗口

我的训练参数设置的是每隔200步生成一次评估日志,每隔1000步保存一次模型,日志中输出的reference_loss值代表了模型的输出与真实值之间的差距,理论来说,这个值越低越好,越低,模型输出的声音就和真人的声音越像,但从经验来说未必如此,过低的loss值也可能代表了模型过拟合,我们只能将这个参数作为一个参考,实际效果要使用测试了这个模型后才能得知,我们可以参考这个值初步选择模型进行推理测试

训练是不会自动终止的,当我们感觉训练的差不多了,想试一下模型的实际效果时,可以在训练终端窗口中键盘键入Ctrl + C停止训练,如果对本次训练出来的模型还不满意,想要继续训练,则可以点击继续上一次的训练进度,程序会从上一个自动保存的模型的进度开始继续训练

推理

加载模型

模型训练完后我们就可以将选项卡切换到推理来测试模型的实际效果了

我们先点击刷新选项,此时在模型选择配置文件的下拉菜单中就出现了我们之前训练好的模型,参考之前的reference_loss值我们选取一个模型(G开头),并且选择训练所使用的配置文件,点击加载模型,等待下面的Output Message文本框出现模型加载成功字样,就可以正式开始我们的推理了

加载模型

开始推理

Sovits是一个声音转换工具,我们首先需要找一段想要转换的原声音频,和准备数据集那一章的要求一样,我们需要一段干声,不能有伴奏、混响、和声。如果想让模型唱歌的话,我们可以采用前面准备数据集所用的方法,直接将原曲去伴奏混响,处理好后将其拖入音频上传区域中

默认的 pm f0预测器推理出来的音质效果最好,所以建议先使用默认参数推理一遍,出现问题再针对性的对参数进行调整

我们点击下面的音频转换按钮,稍等一会儿,在Output Audio那里便会生成一段推理后的音频

推理

我们试听后根据具体的问题设置转换参数:

  • 出现哑音

哑音是因为原声音频中的和声部分没有去处干净,导致f0预测器对音高的预测出现了错误,预测成了一个极高的音高,模型唱不上去导致的

有两种办法解决这个问题,一种就是从源头解决问题,想办法将原声音频的和声混响去除,获得更纯净的干声重新推理,另一种方法可以将f0预测器换成crepe,适度调节F0过滤阈值(一般使用默认值就行,改大改小没什么区别),然后重新进行推理基本就可以解决这个问题,但是音质不如f0预测器pm,电流音等杂音会变多,两种方法大家可以自行判断选取

  • 音域差距过大

如果训练的模型是男声,但推理使用的原声是女声,或者反过来,碰到这种音域差距过大的情况会导致推理出来的音频不堪入耳

我们可以打开f0自动预测选项来解决这个问题,但正如选项里描述的那样,此选择仅限于转换语音时才可用,转换歌声时打开此选项会导致灾难性的跑调

如果是歌声并且实在是想要唱这首歌的话,建议去找一个和自己音域比较契合的翻唱音频,用这个音频处理后作为原声进行推理

  • 部分音调唱不上(下)去

训练时喂的数据集没有覆盖到部分音域

这里的变调选项似乎是先推理再变调?所以对这种情况不起作用,建议使用AU先将原声音频升降调到合适的音域,再进行推理

  • 爆显存

在推理的过程中有可能会出现爆显存的情况,因为推理也是将原音频按照响度阈值切成一段段小切片分别进行推理,最后再合成,如果其中有一段切片时常过长就可能会导致爆显存

我们可以将切片阈值调高,使得原声音频可以切的更加细碎,甚至你也可以直接调整音频自动切片的值开启强制切片,比如输入10,音频就会被切成每10s一段,确保音频时常不会爆显存

合成

如果转换的是歌声,最后可以将转换出来的干声和伴奏合并成一个音频文件,可以使用AU等软件,在合成之前还可以对干声进行一些EQ、混响的调整等,关于音乐方面的知识这里就不多说了

结尾

最后贴一下我自己练的模型所推理出来的歌声,使用了30分钟质量较好的干声素材和30分钟质量较差的素材(噪音和混响比较多),训练了11万步,使用crepef0预测器推理得出

链接:https://www.bilibili.com/audio/au3907000

这是我第一次尝试训练Sovits模型,如果有什么疏漏或错误欢迎大家指出


如何完美监听帧动画?AnimationDrawable深度解析

简介

作为苦逼的程序员,产品和设计提出来的需求咱也没法拒绝,这不,前两天设计就给提了个需求,要求在帧动画结束后,把原位置的动画替换成一段文字。我们知道,在Android中,帧动画的实现类为AnimationDrawable,而这玩意儿又不像Animator一样可以通过addListener之类的方法监听动画的开始、结束等事件,那我们该怎么监听AnimationDrawable的结束事件呢?

目前网上大多数的做法都是获取帧动画的总时长,然后用Handler做一个postDelayed执行结束后的事情。这种方法怎么说呢?能用,但是不够精准也不够优雅,本文我们将从源码层面解析AnimationDrawable是如何将一帧帧的图片组合起来展示成连续的动画的,再从中寻求动画监听的切入点。

注:只想看实现的朋友们可以直接跳到 包装Drawable.Callback 这一节看最终实现

ImageView如何展示Drawable

AnimationDrawable说到底它也就是个Drawable,而我们一般都是使用ImageView作为Drawable展示的布局,那我们就以此作为入口开始分析DrawableImageView中是如何被展示的。

回想一下,我们想要给一个ImageView设置图片一般可以用下面几种方法:

  • setImageBitmap
  • setImageResource
  • setImageURI
  • setImageDrawable

setImageBitmap会将Bitmap包装成一个BitmapDrawable,然后再调用setImageDrawable方法。

setImageResourcesetImageURI方法会通过resolveUri方法从ResourceUri中解析出Drawable,然后调用updateDrawable方法

setImageDrawable方法则会直接调用updateDrawable方法

最终殊途同归走到updateDrawable方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void updateDrawable(Drawable d) {
...
if (mDrawable != null) {
sameDrawable = mDrawable == d;
mDrawable.setCallback(null);
unscheduleDrawable(mDrawable);
...
}

mDrawable = d;

if (d != null) {
d.setCallback(this);
...
} else {
...
}
}

可以看到,这里将我们设置的图片资源赋值到mDrawable上。注意,这里有一个Drawable动起来的关键点,同时也是我们动画监听的最终切入点:Drawable.setCallback(this),我们后面分析帧切换的时候会详细去聊它。

我们知道,一个控件想要绘制内容得在onDraw方法中操作Canvas,所以让我们再来看看onDraw方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

if (mDrawable == null) {
return; // couldn't resolve the URI
}

if (mDrawableWidth == 0 || mDrawableHeight == 0) {
return; // nothing to draw (empty bounds)
}

...
mDrawable.draw(canvas);
...
}

可以看到,这里调用了Drawable.draw方法将Drawable自身绘制到ImageViewCanvas

DrawableContainer

查看AnimationDrawable的继承关系我们可以得知它继承自DrawableContainer,从命名中我们就能看出来,它是Drawable的容器,我们来看一下它所实现的draw方法:

1
2
3
4
5
6
7
8
public void draw(Canvas canvas) {
if (mCurrDrawable != null) {
mCurrDrawable.draw(canvas);
}
if (mLastDrawable != null) {
mLastDrawable.draw(canvas);
}
}

mLastDrawable是为了完成动画的切换效果(出入场动画)所准备的,我们可以不用关心它。

我们可以发现,它的内部有一个名为mCurrDrawable的成员变量,我们可以合理猜测它是通过切换mCurrDrawable指向的目标Drawable来完成展示不同图片的功能,那么事实是这样吗?

没错,DrawableContainer给我们提供了一个selectDrawable方法,用来切换不同的图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public boolean selectDrawable(int index) {
if (index == mCurIndex) {
return false;
}

...

if (index >= 0 && index < mDrawableContainerState.mNumChildren) {
final Drawable d = mDrawableContainerState.getChild(index);
mCurrDrawable = d;
mCurIndex = index;
...
} else {
mCurrDrawable = null;
mCurIndex = -1;
}

...

invalidateSelf();

return true;
}

可以看到,和我们猜想的一样,在DrawableContainer的内部有一个子类DrawableContainerState用于保存所有的Drawable,它继承自Drawable.ConstantState,是用来储存Drawable间的常量状态和数据的。在DrawableContainerState中有一个mDrawables数组用于保存所有的Drawable,通过addChild方法将Drawable加入到这个数组中

而在selectDrawable方法中,它通过getChild方法去获取当前应该显示的Drawable,并将其和index分别赋值给它的两个成员变量mCurrDrawablemCurIndex,然后调用invalidateSelf方法执行重绘:

1
2
3
4
5
6
public void invalidateSelf() {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}

invalidateSelf被定义实现在Drawable类中,还记得我之前让大家注意的Callback吗?在设置图片这一步时,它就被赋值了,实际上这个接口被View所实现,所以在前面我们可以看到调用setCallback时,我们传入的参数为this

不过ImageView在继承View的同时也重写了这个invalidateDrawable方法,最终调用了invalidate方法执行重绘,此时,一张新的图片就被展示到我们的屏幕上了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//ImageView.invalidateDrawable
public void invalidateDrawable(@NonNull Drawable dr) {
if (dr == mDrawable) {
if (dr != null) {
// update cached drawable dimensions if they've changed
final int w = dr.getIntrinsicWidth();
final int h = dr.getIntrinsicHeight();
if (w != mDrawableWidth || h != mDrawableHeight) {
mDrawableWidth = w;
mDrawableHeight = h;
// updates the matrix, which is dependent on the bounds
configureBounds();
}
}
/* we invalidate the whole view in this case because it's very
* hard to know where the drawable actually is. This is made
* complicated because of the offsets and transformations that
* can be applied. In theory we could get the drawable's bounds
* and run them through the transformation and offsets, but this
* is probably not worth the effort.
*/
invalidate();
} else {
super.invalidateDrawable(dr);
}
}

AnimationDrawable

DrawableContainer分析完后,我们可以很自然的想到,AnimationDrawable就是通过DrawableContainer这种可以切换图片的机制,每隔一定时间执行一下selectDrawable便可以达成帧动画的效果了。

我们先回想一下,在代码中怎么构造出一个多帧的AnimationDrawable?没错,用默认构造方法实例化出来后,调用它的addFrame方法往里一帧帧的添加图片:

1
2
3
4
5
6
public void addFrame(@NonNull Drawable frame, int duration) {
mAnimationState.addFrame(frame, duration);
if (!mRunning) {
setFrame(0, true, false);
}
}

可以看到AnimationDrawable也有一个内部类AnimationState,继承自DrawableContainerState,它的addFrame方法就是调用DrawableContainerState.addChild方法添加图片,同时将这张图片的持续时间保存在mDurations数组中:

1
2
3
4
public void addFrame(Drawable dr, int dur) {
int pos = super.addChild(dr);
mDurations[pos] = dur;
}

想让AnimationDrawable动起来的话,我们得要调用它的start方法,那我们就从这个方法开始分析:

1
2
3
4
5
6
7
8
9
public void start() {
mAnimating = true;

if (!isRunning()) {
// Start from 0th frame.
setFrame(0, false, mAnimationState.getChildCount() > 1
|| !mAnimationState.mOneShot);
}
}

这里将mAnimating状态置为true,然后调用setFrame方法从第0帧开始展示图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void setFrame(int frame, boolean unschedule, boolean animate) {
if (frame >= mAnimationState.getChildCount()) {
return;
}
mAnimating = animate;
mCurFrame = frame;
selectDrawable(frame);
if (unschedule || animate) {
unscheduleSelf(this);
}
if (animate) {
// Unscheduling may have clobbered these values; restore them
mCurFrame = frame;
mRunning = true;
scheduleSelf(this, SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]);
}
}

这里可以看到,和我们所想的一样,调用了DrawableContainer.selectDrawable切换当前展示图片,由于我们之前将mAnimating赋值为了true,所以会调用scheduleSelf方法调度展示下一张图片,时间为当前帧持续时间后

1
2
3
4
5
6
public void scheduleSelf(@NonNull Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
callback.scheduleDrawable(this, what, when);
}
}

scheduleSelf方法调用了Drawable.Callback.scheduleDrawable方法,我们去View里面看实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
if (verifyDrawable(who) && what != null) {
final long delay = when - SystemClock.uptimeMillis();
if (mAttachInfo != null) {
mAttachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed(
Choreographer.CALLBACK_ANIMATION, what, who,
Choreographer.subtractFrameDelay(delay));
} else {
// Postpone the runnable until we know
// on which thread it needs to run.
getRunQueue().postDelayed(what, delay);
}
}
}

实际上两个分支最终都是通过Handler实现延时调用,而调用的Runnable对象就是之前scheduleSelf传入的this。没错,AnimationDrawable实现了Runnable接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void run() {
nextFrame(false);
}

private void nextFrame(boolean unschedule) {
int nextFrame = mCurFrame + 1;
final int numFrames = mAnimationState.getChildCount();
final boolean isLastFrame = mAnimationState.mOneShot && nextFrame >= (numFrames - 1);

// Loop if necessary. One-shot animations should never hit this case.
if (!mAnimationState.mOneShot && nextFrame >= numFrames) {
nextFrame = 0;
}

setFrame(nextFrame, unschedule, !isLastFrame);
}

可以看到,在一帧持续时间结束后,便会调用nextFrame方法,计算下一帧的index,然后调用setFrame方法切换下一帧,形成一个循环,这样一帧帧的图片便动了起来,形成了帧动画

包装Drawable.Callback

我们从源码层面分析了帧动画是如何运作的,那么怎么监听动画事件相信各位应该都能得出结论了吧?没错,就是重设DrawableCallback

Drawable被设置到控件中后,控件会将自身作为Drawable.Callback设置给Drawable,那么我们只需要重新给Drawable设置一个Drawable.Callback,在其中调用View回调方法的同时,加入自己的监听逻辑即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
val animDrawable = imageView.drawable as AnimationDrawable
val callback = object : Drawable.Callback {
override fun invalidateDrawable(who: Drawable) {
imageView.invalidateDrawable(who)
if (animDrawable.getFrame(animDrawable.numberOfFrames - 1) == current
&& animDrawable.isOneShot
&& animDrawable.isRunning
&& animDrawable.isVisible
) {
val lastFrameDuration = getDuration(animDrawable.numberOfFrames - 1)
postDelayed({ ...//结束后需要做的事 }, lastFrameDuration.toLong())
}
}

override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {
imageView.scheduleDrawable(who, what, `when`)
}

override fun unscheduleDrawable(who: Drawable, what: Runnable) {
imageView.unscheduleDrawable(who, what)
}
}
//注意一定需要用一个成员变量或其他方式持有这个Callback
//因为Drawable.Callback是以弱引用的形式被保存在Drawable内的,很容易被回收
mCallbackHolder = callback
animDrawable.callback = callback
animDrawable.start()

以上的代码便是示例,当满足动画运行到最后一帧,且满足结束状态时,在最后一帧的持续时间后处理结束后需要做的事

AnimationDrawable切换Visible状态为false时,动画会被暂停,如果在动画结束后触发setVisible(false)事件,也会触发invalidateDrawable回调,所以这里需要额外判断一下isVisible

自己包装的Drawable.Callback一定需要找个东西将它强引用起来,因为Drawable.Callback是以弱引用的形式被保存在Drawable内的,很容易被回收,一旦被回收,整个AnimationDrawable动画就动不起来了

尾声

为了这么简单一个小功能,还得跑到源码里看怎么实现,对此我的感受是:一入安卓深似海,从此头发是路人


Android动态权限申请从未如此简单

前言

注:只想看实现的朋友们可以直接跳到最后面的最终实现

大家是否还在为动态权限申请感到苦恼呢?传统的动态权限申请需要在Activity中重写onRequestPermissionsResult方法来接收用户权限授予的结果。试想一下,你需要在一个子模块中申请权限,那得从这个模块所在的ActivityonRequestPermissionsResult中将结果一层层再传回到这个模块中,相当的麻烦,代码也相当冗余和不干净,逼死强迫症。

使用

为了解决这个痛点,我封装出了两个方法,用于随时随地快速的动态申请权限,我们先来看看我们的封装方法是如何调用的:

1
2
3
4
5
6
7
8
activity.requestPermission(Manifest.permission.CAMERA, onPermit = {
//申请权限成功 Do something
}, onDeny = { shouldShowCustomRequest ->
//申请权限失败 Do something
if (shouldShowCustomRequest) {
//用户选择了拒绝并且不在询问,此时应该使用自定义弹窗提醒用户授权(可选)
}
})

这样是不是非常的简单便捷?申请和结果回调都在一个方法内处理,并且支持随用随调。

方案

那么,这么方便好用的方法是怎么实现的呢?不知道小伙伴们在平时开发中有没有注意到过,当你调用startActivityForResult时,AS会提示你该方法已被弃用,点进去看会告诉你应该使用registerForActivityResult方法替代。没错,这就是androidx给我们提供的ActivityResult功能,并且这个功能不仅支持ActivityResult回调,还支持打开文档,拍摄照片,选择文件等各种各样的回调,同样也包括我们今天要说的权限申请

其实Android在官方文档 请求运行时权限 中就已经将其作为动态权限申请的推荐方法了,如下示例代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
val requestPermissionLauncher =
registerForActivityResult(RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
// Permission is granted. Continue the action or workflow in your
// app.
} else {
// Explain to the user that the feature is unavailable because the
// feature requires a permission that the user has denied. At the
// same time, respect the user's decision. Don't link to system
// settings in an effort to convince the user to change their
// decision.
}
}

when {
ContextCompat.checkSelfPermission(
CONTEXT,
Manifest.permission.REQUESTED_PERMISSION
) == PackageManager.PERMISSION_GRANTED -> {
// You can use the API that requires the permission.
}
shouldShowRequestPermissionRationale(...) -> {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected, and what
// features are disabled if it's declined. In this UI, include a
// "cancel" or "no thanks" button that lets the user continue
// using your app without granting the permission.
showInContextUI(...)
}
else -> {
// You can directly ask for the permission.
// The registered ActivityResultCallback gets the result of this request.
requestPermissionLauncher.launch(
Manifest.permission.REQUESTED_PERMISSION)
}
}

说到这里,可能有小伙伴要质疑我了:“官方文档里都写明了的东西,你还特地写一遍,还起了这么个标题,是不是在水文章?!”

莫急,如果你遵照以上方法这么写的话,在实际调用的时候会直接发生崩溃:

1
2
3
java.lang.IllegalStateException: 
LifecycleOwner Activity is attempting to register while current state is RESUMED.
LifecycleOwners must call register before they are STARTED.

这段报错很明显的告诉我们,我们的注册工作必须要在Activity声明周期STARTED之前进行(也就是onCreate时和onStart完成前),但这样我们就必须要事先注册好所有可能会用到的权限,没办法做到随时随地有需要时再申请权限了,有办法解决这个问题吗?答案是肯定的。

绕过生命周期检测

想解决这个问题,我们必须要知道问题的成因,让我们带着问题进到源码中一探究竟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull ActivityResultContract<I, O> contract,
@NonNull ActivityResultCallback<O> callback) {
return registerForActivityResult(contract, mActivityResultRegistry, callback);
}

public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultRegistry registry,
@NonNull final ActivityResultCallback<O> callback) {
return registry.register(
"activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback);
}

public final <I, O> ActivityResultLauncher<I> register(
@NonNull final String key,
@NonNull final LifecycleOwner lifecycleOwner,
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultCallback<O> callback) {

Lifecycle lifecycle = lifecycleOwner.getLifecycle();

if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
throw new IllegalStateException("LifecycleOwner " + lifecycleOwner + " is "
+ "attempting to register while current state is "
+ lifecycle.getCurrentState() + ". LifecycleOwners must call register before "
+ "they are STARTED.");
}

registerKey(key);
LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
if (lifecycleContainer == null) {
lifecycleContainer = new LifecycleContainer(lifecycle);
}
LifecycleEventObserver observer = new LifecycleEventObserver() { ... };
lifecycleContainer.addObserver(observer);
mKeyToLifecycleContainers.put(key, lifecycleContainer);

return new ActivityResultLauncher<I>() { ... };
}

我们可以发现,registerForActivityResult实际上就是调用了ComponentActivity内部成员变量的mActivityResultRegistry.register方法,而在这个方法的一开头就检查了当前Activity的生命周期,如果生命周期位于STARTED后则直接抛出异常,那我们该如何绕过这个限制呢?

其实在register方法的下面就有一个同名重载方法,这个方法并没有做生命周期的检测:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final <I, O> ActivityResultLauncher<I> register(
@NonNull final String key,
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultCallback<O> callback) {
registerKey(key);
mKeyToCallback.put(key, new CallbackAndContract<>(callback, contract));

if (mParsedPendingResults.containsKey(key)) {
@SuppressWarnings("unchecked")
final O parsedPendingResult = (O) mParsedPendingResults.get(key);
mParsedPendingResults.remove(key);
callback.onActivityResult(parsedPendingResult);
}
final ActivityResult pendingResult = mPendingResults.getParcelable(key);
if (pendingResult != null) {
mPendingResults.remove(key);
callback.onActivityResult(contract.parseResult(
pendingResult.getResultCode(),
pendingResult.getData()));
}

return new ActivityResultLauncher<I>() { ... };
}

找到这个方法就简单了,我们将registerForActivityResult方法调用替换成activityResultRegistry.register调用就可以了

当然,我们还需要注意一些小细节,检查生命周期的register方法同时也会注册生命周期回调,当Activity被销毁时会将我们注册的ActivityResult回调移除,我们也需要给我们封装的方法加上这个逻辑,最终实现就如下所示。

最终实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
private val nextLocalRequestCode = AtomicInteger()

private val nextKey: String
get() = "activity_rq#${nextLocalRequestCode.getAndIncrement()}"

fun ComponentActivity.requestPermission(
permission: String,
onPermit: () -> Unit,
onDeny: (shouldShowCustomRequest: Boolean) -> Unit
) {
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
onPermit()
return
}
var launcher by Delegates.notNull<ActivityResultLauncher<String>>()
launcher = activityResultRegistry.register(
nextKey,
ActivityResultContracts.RequestPermission()
) { result ->
if (result) {
onPermit()
} else {
onDeny(!ActivityCompat.shouldShowRequestPermissionRationale(this, permission))
}
launcher.unregister()
}
lifecycle.addObserver(object : LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_DESTROY) {
launcher.unregister()
lifecycle.removeObserver(this)
}
}
})
launcher.launch(permission)
}

fun ComponentActivity.requestPermissions(
permissions: Array<String>,
onPermit: () -> Unit,
onDeny: (shouldShowCustomRequest: Boolean) -> Unit
) {
var hasPermissions = true
for (permission in permissions) {
if (ContextCompat.checkSelfPermission(
this,
permission
) != PackageManager.PERMISSION_GRANTED
) {
hasPermissions = false
break
}
}
if (hasPermissions) {
onPermit()
return
}
var launcher by Delegates.notNull<ActivityResultLauncher<Array<String>>>()
launcher = activityResultRegistry.register(
nextKey,
ActivityResultContracts.RequestMultiplePermissions()
) { result ->
var allAllow = true
for (allow in result.values) {
if (!allow) {
allAllow = false
break
}
}
if (allAllow) {
onPermit()
} else {
var shouldShowCustomRequest = false
for (permission in permissions) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
shouldShowCustomRequest = true
break
}
}
onDeny(shouldShowCustomRequest)
}
launcher.unregister()
}
lifecycle.addObserver(object : LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_DESTROY) {
launcher.unregister()
lifecycle.removeObserver(this)
}
}
})
launcher.launch(permissions)
}

总结

其实很多实用技巧本质上都是很简单的,但没有接触过就很难想到,我将我的开发经验分享给大家,希望能帮助到大家。


Android源码分析 - Activity销毁流程

开篇

本篇以android-11.0.0_r25作为基础解析

我们在之前的几篇Activity启动流程分析中已经了解了Activity一半的生命周期,接下来这篇文章我们就来分析一下Activity销毁相关的生命周期

前几期文章回顾:

Android源码分析 - Activity启动流程(上)

Android源码分析 - Activity启动流程(中)

Android源码分析 - Activity启动流程(下)

触发销毁

既然要分析Activity销毁流程,那我们就从最常见的入口Activity.finish入手

1
2
3
public void finish() {
finish(DONT_FINISH_TASK_WITH_ACTIVITY);
}

默认的finish方法调用了另一个同名重载方法,接受一个int类型的参数表明是否需要在销毁Activity的同时销毁Task,该参数有以下三种:

  • DONT_FINISH_TASK_WITH_ACTIVITY:默认参数,表示在销毁Activity的时候不要销毁Task

  • FINISH_TASK_WITH_ROOT_ACTIVITY:当Activity为跟Activity的时候,销毁的同时销毁Task,同时这个任务也会从最近任务中移除

  • FINISH_TASK_WITH_ACTIVITY:销毁Activity的时候同时销毁Task,但不会从最近任务中移除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void finish(int finishTask) {
if (mParent == null) {
//当finish后才可能会触发onActivityResult回调
//这里准备将result返回给之前调用startActivityForResult的Activity
int resultCode;
Intent resultData;
synchronized (this) {
resultCode = mResultCode;
resultData = mResultData;
}
try {
//两个Activity可能处于不同进程中,做进程间通信的准备
if (resultData != null) {
resultData.prepareToLeaveProcess(this);
}
//调用ATMS销毁Activity
if (ActivityTaskManager.getService()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
} catch (RemoteException e) {
// Empty
}
} else {
mParent.finishFromChild(this);
}

// Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
// be restored now.
if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
restoreAutofillSaveUi();
}
}

onActivityResult回调是在对应Activity resume时才可能触发,具体过程后面会分析,将ActivityRecord.TokenResult作为参数调用ATMS.finishActivity方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
int finishTask) {
// Refuse possible leaked file descriptors
//回传的ResultIntent中不允许包含fd,防止泄漏
if (resultData != null && resultData.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}

final ActivityRecord r;
synchronized (mGlobalLock) {
//获取ActivityRecord并保证其在栈中
r = ActivityRecord.isInStackLocked(token);
//为null说明已被移出ActivityStack,视作已被finish
if (r == null) {
return true;
}
}

// Carefully collect grants without holding lock
//检查调用方(即待finish的Activity)是否能授予result所对应Activity package访问uri的权限
final NeededUriGrants resultGrants = collectGrants(resultData, r.resultTo);

synchronized (mGlobalLock) {
// Sanity check in case activity was removed before entering global lock.
if (!r.isInHistory()) {
return true;
}

// Keep track of the root activity of the task before we finish it
final Task tr = r.getTask();
final ActivityRecord rootR = tr.getRootActivity();
// Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
// finish.
//LockTask模式下,如果此为最后一个Task,则不允许被销毁
//详见:https://developer.android.com/work/dpc/dedicated-devices/lock-task-mode
if (getLockTaskController().activityBlockedFromFinish(r)) {
return false;
}

// TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked
// We should consolidate.
//IActivityController分发Activity状态变化
if (mController != null) {
// Find the first activity that is not finishing.
//寻找该Activity销毁后的下一个顶层Activity
final ActivityRecord next =
r.getRootTask().topRunningActivity(token, INVALID_TASK_ID);
if (next != null) {
// ask watcher if this is allowed
boolean resumeOK = true;
try {
resumeOK = mController.activityResuming(next.packageName);
} catch (RemoteException e) {
mController = null;
Watchdog.getInstance().setActivityController(null);
}

if (!resumeOK) {
return false;
}
}
}

// note down that the process has finished an activity and is in background activity
// starts grace period
//设置Activity销毁的最新时间
if (r.app != null) {
r.app.setLastActivityFinishTimeIfNeeded(SystemClock.uptimeMillis());
}

final long origId = Binder.clearCallingIdentity();
try {
boolean res;
final boolean finishWithRootActivity =
finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
|| (finishWithRootActivity && r == rootR)) { //需要同时销毁Task
// If requested, remove the task that is associated to this activity only if it
// was the root activity in the task. The result code and data is ignored
// because we don't support returning them across task boundaries. Also, to
// keep backwards compatibility we remove the task from recents when finishing
// task with root activity.
//移除Task
mStackSupervisor.removeTask(tr, false /*killProcess*/,
finishWithRootActivity, "finish-activity");
res = true;
// Explicitly dismissing the activity so reset its relaunch flag.
r.mRelaunchReason = RELAUNCH_REASON_NONE;
} else { //不需要同时销毁Task
r.finishIfPossible(resultCode, resultData, resultGrants,
"app-request", true /* oomAdj */);
res = r.finishing;
}
return res;
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}

这个方法里分了两个case,当需要同时销毁Task的时候,直接调用ActivityStackSupervisor.removeTask,当不需要同时销毁Task的时候,调用ActivityRecord.finishIfPossible

我们先看需要同时销毁Task的case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) {
if (task.mInRemoveTask) {
// Prevent recursion.
return;
}
task.mInRemoveTask = true;
try {
//执行Task移除操作
task.performClearTask(reason);
//对Task执行杀进程,从最近任务列表移除等操作
cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
//关闭LockTask模式
mService.getLockTaskController().clearLockedTask(task);
//通知Task状态发生变化
mService.getTaskChangeNotificationController().notifyTaskStackChanged();
//将最近任务持久化保存
if (task.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
} finally {
task.mInRemoveTask = false;
}
}

本篇文章我们主要关注的是Activity销毁流程,至于进程的关闭,最近任务列表的更新我们在这里就不关心了,而这里Activity销毁的重点在于Task.performClearTask方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/** Completely remove all activities associated with an existing task. */
void performClearTask(String reason) {
// Broken down into to cases to avoid object create due to capturing mStack.
if (getStack() == null) {
forAllActivities((r) -> {
if (r.finishing) return;
// Task was restored from persistent storage.
r.takeFromHistory();
removeChild(r);
});
} else {
forAllActivities((r) -> {
if (r.finishing) return;
// TODO: figure-out how to avoid object creation due to capture of reason variable.
r.finishIfPossible(Activity.RESULT_CANCELED,
null /* resultData */, null /* resultGrants */, reason, false /* oomAdj */);
});
}
}

我们看后半部分代码可以发现,这个方法对Task中所有未销毁的Activity都执行了ActivityRecord.finishIfPossible方法,这样路径就和上面ATMS.finishActivity方法中第二个case统一了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
* Finish activity if possible. If activity was resumed - we must first pause it to make the
* activity below resumed. Otherwise we will try to complete the request immediately by calling
* {@link #completeFinishing(String)}.
* @return One of {@link FinishRequest} values:
* {@link #FINISH_RESULT_REMOVED} if this activity has been removed from the history list.
* {@link #FINISH_RESULT_REQUESTED} if removal process was started, but it is still in the list
* and will be removed from history later.
* {@link #FINISH_RESULT_CANCELLED} if activity is already finishing or in invalid state and the
* request to finish it was not ignored.
*/
@FinishRequest int finishIfPossible(int resultCode, Intent resultData,
NeededUriGrants resultGrants, String reason, boolean oomAdj) {

//防止重复销毁
if (finishing) {
return FINISH_RESULT_CANCELLED;
}

//此Activity不在任务栈中
if (!isInStackLocked()) {
return FINISH_RESULT_CANCELLED;
}

final ActivityStack stack = getRootTask();
//应该调整顶部Activity
final boolean mayAdjustTop = (isState(RESUMED) || stack.mResumedActivity == null)
&& stack.isFocusedStackOnDisplay();
//应该调整全局焦点
final boolean shouldAdjustGlobalFocus = mayAdjustTop
// It must be checked before {@link #makeFinishingLocked} is called, because a stack
// is not visible if it only contains finishing activities.
&& mRootWindowContainer.isTopDisplayFocusedStack(stack);

//暂停布局工作
mAtmService.deferWindowLayout();
try {
//设置当前Activity状态为finishing
makeFinishingLocked();
// Make a local reference to its task since this.task could be set to null once this
// activity is destroyed and detached from task.
final Task task = getTask();
//获取上一个ActivityRecord
ActivityRecord next = task.getActivityAbove(this);
//传递FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET:重置该Task时清除此Activity
if (next != null) {
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
// If the caller asked that this activity (and all above it)
// be cleared when the task is reset, don't lose that information,
// but propagate it up to the next activity.
next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
}

//暂停输入事件分发
pauseKeyDispatchingLocked();

// We are finishing the top focused activity and its task has nothing to be focused so
// the next focusable task should be focused.
//应该调整顶部Activity,但此Task没有Activity可以被运行在顶部,将焦点转移至下一个可聚焦的Task
if (mayAdjustTop && ((ActivityStack) task).topRunningActivity(true /* focusableOnly */)
== null) {
task.adjustFocusToNextFocusableTask("finish-top", false /* allowFocusSelf */,
shouldAdjustGlobalFocus);
}

//将Result信息写入到对应ActivityRecord中,待后面resume的时候触发onActivityResult回调
finishActivityResults(resultCode, resultData, resultGrants);

//终止Task
final boolean endTask = task.getActivityBelow(this) == null
&& !task.isClearingToReuseTask();
final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
if (isState(RESUMED)) {
if (endTask) {
//通知Task移除已开始
mAtmService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
task.getTaskInfo());
}
// Prepare app close transition, but don't execute just yet. It is possible that
// an activity that will be made resumed in place of this one will immediately
// launch another new activity. In this case current closing transition will be
// combined with open transition for the new activity.
//准备Activity转场动画
mDisplayContent.prepareAppTransition(transit, false);

// When finishing the activity preemptively take the snapshot before the app window
// is marked as hidden and any configuration changes take place
//更新Task快照
if (mAtmService.mWindowManager.mTaskSnapshotController != null) {
final ArraySet<Task> tasks = Sets.newArraySet(task);
mAtmService.mWindowManager.mTaskSnapshotController.snapshotTasks(tasks);
mAtmService.mWindowManager.mTaskSnapshotController
.addSkipClosingAppSnapshotTasks(tasks);
}

// Tell window manager to prepare for this one to be removed.
//设置可见性
setVisibility(false);

if (stack.mPausingActivity == null) {
//开始暂停此Activity
stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
null /* resuming */);
}

if (endTask) {
//屏幕固定功能
mAtmService.getLockTaskController().clearLockedTask(task);
// This activity was in the top focused stack and this is the last activity in
// that task, give this activity a higher layer so it can stay on top before the
// closing task transition be executed.
//更新窗口层级
if (mayAdjustTop) {
mNeedsZBoost = true;
mDisplayContent.assignWindowLayers(false /* setLayoutNeeded */);
}
}
} else if (!isState(PAUSING)) {
... //正常不会进入此case
}

return FINISH_RESULT_REQUESTED;
} finally {
//恢复布局工作
mAtmService.continueWindowLayout();
}
}

这个方法中,我们需要关注一下对于Result信息的处理,这里调用了finishActivityResults方法,将Result信息写入到对应ActivityRecord中,待后面resume的时候触发onActivityResult回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* Sets the result for activity that started this one, clears the references to activities
* started for result from this one, and clears new intents.
*/
private void finishActivityResults(int resultCode, Intent resultData,
NeededUriGrants resultGrants) {
// Send the result if needed
if (resultTo != null) {
if (resultTo.mUserId != mUserId) {
if (resultData != null) {
resultData.prepareToLeaveUser(mUserId);
}
}
if (info.applicationInfo.uid > 0) {
mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(resultGrants,
resultTo.getUriPermissionsLocked());
}
resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData);
resultTo = null;
}

// Make sure this HistoryRecord is not holding on to other resources,
// because clients have remote IPC references to this object so we
// can't assume that will go away and want to avoid circular IPC refs.
results = null;
pendingResults = null;
newIntents = null;
setSavedState(null /* savedState */);
}

//将Result结果添加到results列表中
void addResultLocked(ActivityRecord from, String resultWho,
int requestCode, int resultCode,
Intent resultData) {
ActivityResult r = new ActivityResult(from, resultWho,
requestCode, resultCode, resultData);
if (results == null) {
results = new ArrayList<ResultInfo>();
}
results.add(r);
}

这个方法很简单,就是将Result信息添加到ActivityRecord.results列表中

然后我们继续沿着finish主线链路走,后面有一个isState的判断,正常来说,ActivityRecordstate应该为RESUMED,具体为什么我们可以回顾一下之前分析的Activity启动流程,在ActivityStackSupervisor.realStartActivityLocked方法最后,会调用ActivityStack.minimalResumeActivityLocked,在这个方法中,会将ActivityRecordstate设置为RESUMED,由于ClientTransaction的执行是通过Handler.sendMessage进行的,所以早在Activity onCreate之前,ActivityRecord的状态就已经被设为了RESUMED

根据以上分析,我们会走进isState(RESUMED)这个case中,接着调用ActivityStack.startPausingLocked方法暂停Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
* Start pausing the currently resumed activity. It is an error to call this if there
* is already an activity being paused or there is no resumed activity.
*
* @param userLeaving True if this should result in an onUserLeaving to the current activity.
* @param uiSleeping True if this is happening with the user interface going to sleep (the
* screen turning off).
* @param resuming The activity we are currently trying to resume or null if this is not being
* called as part of resuming the top activity, so we shouldn't try to instigate
* a resume here if not null.
* @return Returns true if an activity now is in the PAUSING state, and we are waiting for
* it to tell us when it is done.
*/
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
ActivityRecord resuming) {
//已有Activity正在暂停中
if (mPausingActivity != null) {
if (!shouldSleepActivities()) {
// Avoid recursion among check for sleep and complete pause during sleeping.
// Because activity will be paused immediately after resume, just let pause
// be completed by the order of activity paused from clients.
completePauseLocked(false, resuming);
}
}
//上一个已resume的Activity
ActivityRecord prev = mResumedActivity;

//既没有已resume的Activity,也没有正在resume的Activity
//从栈顶找一个Activity恢复
if (prev == null) {
if (resuming == null) {
mRootWindowContainer.resumeFocusedStacksTopActivities();
}
return false;
}

//不能暂停一个正在resume的Activity
if (prev == resuming) {
return false;
}

//设置各种状态
mPausingActivity = prev;
mLastPausedActivity = prev;
mLastNoHistoryActivity = prev.isNoHistory() ? prev : null;
prev.setState(PAUSING, "startPausingLocked");
prev.getTask().touchActiveTime();
clearLaunchTime(prev);

//更新统计信息
mAtmService.updateCpuStats();

boolean pauseImmediately = false;
... //当前流程下pauseImmediately始终为false

if (prev.attachedToProcess()) {
try {
//调度Pause生命周期事务
mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
} else {
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}

// If we are not going to sleep, we want to ensure the device is
// awake until the next activity is started.
//获取Wakelock,确保设备awake状态直到下一个Activity启动
if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
mStackSupervisor.acquireLaunchWakelock();
}

if (mPausingActivity != null) {
// Have the window manager pause its key dispatching until the new
// activity has started. If we're pausing the activity just because
// the screen is being turned off and the UI is sleeping, don't interrupt
// key dispatch; the same activity will pick it up again on wakeup.
if (!uiSleeping) {
//暂停输入事件分发
prev.pauseKeyDispatchingLocked();
}

if (pauseImmediately) { //不会进入此case
// If the caller said they don't want to wait for the pause, then complete
// the pause now.
completePauseLocked(false, resuming);
return false;
} else {
//设置超时监听(500ms内没有完成便视为超时)
prev.schedulePauseTimeout();
return true;
}

} else {
// This activity failed to schedule the
// pause, so just treat it as being paused now.
//未能成功暂停此Activity,从栈顶找一个Activity恢复
if (resuming == null) {
mRootWindowContainer.resumeFocusedStacksTopActivities();
}
return false;
}
}

可以看到,和Activity启动流程类似,该方法里面调用了ClientLifecycleManager.scheduleTransaction方法来调度Activity暂停的生命周期,具体是怎样调度的可以看我之前的文章 Android源码分析 - Activity启动流程(下),里面分析了ClientTransaction事务是怎么被调度执行的

了解完后我们就可以知道,生命周期事务的执行也就相当于分别调用ActivityLifecycleItempreExecuteexecutepostExecute方法,而PauseActivityItem没有重写preExecute方法,所以我们就依次分析其executepostExecute方法就好了

1
2
3
4
5
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,
"PAUSE_ACTIVITY_ITEM");
}

ClientTransactionHandler这个我们之前说过,这是一个抽象类,被ActivityThread继承实现,所以这里实际上就是调用ActivityThread.handlePauseActivity方法

handlePauseActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
int configChanges, PendingTransactionActions pendingActions, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
...
r.activity.mConfigChangeFlags |= configChanges;
performPauseActivity(r, finished, reason, pendingActions);

// Make sure any pending writes are now committed.
//确保所有全局任务都被处理完成
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
//更新标记
mSomeActivitiesChanged = true;
}
}

/**
* Pause the activity.
* @return Saved instance state for pre-Honeycomb apps if it was saved, {@code null} otherwise.
*/
private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,
PendingTransactionActions pendingActions) {
... //异常状态检查
if (finished) {
r.activity.mFinished = true;
}

// Pre-Honeycomb apps always save their state before pausing
//是否需要保存状态信息(Android 3.0前无论是否finish都会触发保存)
final boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb();
if (shouldSaveState) {
//回调Activity的onSaveInstanceState方法
callActivityOnSaveInstanceState(r);
}

performPauseActivityIfNeeded(r, reason);

...//回调OnActivityPausedListener,目前看来只有NFC部分有注册这个回调

... //Android 3.0之前的特殊处理

//返回保存状态的Bundle
return shouldSaveState ? r.state : null;
}

private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
//已暂停,直接返回
if (r.paused) {
// You are already paused silly...
return;
}

// Always reporting top resumed position loss when pausing an activity. If necessary, it
// will be restored in performResumeActivity().
//报告resume状态变更
reportTopResumedActivityChanged(r, false /* onTop */, "pausing");

try {
r.activity.mCalled = false;
//回调Activity的onPause方法
mInstrumentation.callActivityOnPause(r.activity);
if (!r.activity.mCalled) {
//必须要调用super.onPause方法
throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent)
+ " did not call through to super.onPause()");
}
} catch ...
//设置状态
r.setState(ON_PAUSE);
}

这一条调用链路看下来还是很简单的,和之前我们分析过的其他生命周期调用是一个套路,这里显示调用了callActivityOnSaveInstanceState方法保存状态信息

1
2
3
4
5
6
7
8
9
10
11
private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
r.state = new Bundle();
r.state.setAllowFds(false);
if (r.isPersistable()) {
r.persistentState = new PersistableBundle();
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
}
}

通过Instrumentation调用Activity.performSaveInstanceState方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
final void performSaveInstanceState(@NonNull Bundle outState) {
//分发PreSaveInstanceState事件,执行所有注册的ActivityLifecycleCallbacks的onActivityPreSaveInstanceState回调
dispatchActivityPreSaveInstanceState(outState);
//回调onSaveInstanceState
onSaveInstanceState(outState);
//保存受管理的Dialog的状态
saveManagedDialogs(outState);
//共享元素动画相关
mActivityTransitionState.saveState(outState);
//保存权限请求状态
storeHasCurrentPermissionRequest(outState);
//分发PostSaveInstanceState事件,执行所有注册的ActivityLifecycleCallbacks的onActivityPostSaveInstanceState回调
dispatchActivityPostSaveInstanceState(outState);
}

最终回调Activity.onSaveInstanceState方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected void onSaveInstanceState(@NonNull Bundle outState) {
//保存窗口信息
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
//保存Fragment状态
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
//自动填充相关
if (mAutoFillResetNeeded) {
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
//分发SaveInstanceState事件,执行所有注册的ActivityLifecycleCallbacks的onActivitySaveInstanceState回调
dispatchActivitySaveInstanceState(outState);
}

保存状态的流程就基本完成了,我们再回过头来看onPause的触发

在上面performPauseActivityIfNeeded方法中有一行代码调用了Instrumentation.callActivityOnPause方法,
通过Instrumentation调用了Activity.performPause方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
final void performPause() {
//分发PrePaused事件,执行所有注册的ActivityLifecycleCallbacks的onActivityPrePaused回调
dispatchActivityPrePaused();
mDoReportFullyDrawn = false;
//FragmentManager分发pause状态
mFragments.dispatchPause();
mCalled = false;
//回调onPause
onPause();
mResumed = false;
//Target Sdk 9以上(Android 2.3)需要保证在onPause中调用super.onPause方法
if (!mCalled && getApplicationInfo().targetSdkVersion
>= android.os.Build.VERSION_CODES.GINGERBREAD) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onPause()");
}
//分发PostPaused事件,执行所有注册的ActivityLifecycleCallbacks的onActivityPostPaused回调
dispatchActivityPostPaused();
}

执行onPause回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected void onPause() {
//分发Paused事件,执行所有注册的ActivityLifecycleCallbacks的onActivityPaused回调
dispatchActivityPaused();
//自动填充相关
if (mAutoFillResetNeeded) {
if (!mAutoFillIgnoreFirstResumePause) {
View focus = getCurrentFocus();
if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
getAutofillManager().notifyViewExited(focus);
}
} else {
// reset after first pause()
mAutoFillIgnoreFirstResumePause = false;
}
}
//内容捕获服务
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_PAUSE);
//super.onPause标注为已被执行
mCalled = true;
}

到此为止,ActivityonPause生命周期已经基本走完了,此时我们再回到PauseActivityItem.postExecute方法中做一些善后处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void postExecute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
//mDontReport为我们之前obtain方法中传入的pauseImmediately参数,始终为false
if (mDontReport) {
return;
}
try {
// TODO(lifecycler): Use interface callback instead of AMS.
//调用ATMS.activityPaused方法
ActivityTaskManager.getService().activityPaused(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}

这里调用ATMS.activityPaused方法回到system_server进程处理Activity暂停后的事项

1
2
3
4
5
6
7
8
9
10
11
public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
//通过ActivityRecord.Token获取ActivityRecord
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
r.activityPaused(false);
}
}
Binder.restoreCallingIdentity(origId);
}

调用ActivityRecord.activityPaused方法继续处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void activityPaused(boolean timeout) {
final ActivityStack stack = getStack();

if (stack != null) {
//移除超时监听
removePauseTimeout();

if (stack.mPausingActivity == this) {
//暂停布局工作
mAtmService.deferWindowLayout();
try {
stack.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
} finally {
//恢复布局工作
mAtmService.continueWindowLayout();
}
return;
} else { //暂停Activity失败
if (isState(PAUSING)) {
setState(PAUSED, "activityPausedLocked");
if (finishing) {
completeFinishing("activityPausedLocked");
}
}
}
}

//更新Activity可见性
mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}

正常情况下会进入到ActivityStack.completePauseLocked方法中,但在暂停Activity失败的情况下,如果当前状态为PAUSING,则直接将其状态置为PAUSED已暂停,如果被标记为finishing,则会调用ActivityRecord.completeFinishing继续finish流程,这其实和正常情况下的调用链路差不多,具体我们往下就能看到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
ActivityRecord prev = mPausingActivity;

if (prev != null) {
prev.setWillCloseOrEnterPip(false);
//之前的状态是否为正在停止
final boolean wasStopping = prev.isState(STOPPING);
//设置状态为已暂停
prev.setState(PAUSED, "completePausedLocked");
if (prev.finishing) {
//继续finish流程
prev = prev.completeFinishing("completePausedLocked");
} else if (prev.hasProcess()) {
//Configuration发生变化时可能会设置这个flag
if (prev.deferRelaunchUntilPaused) {
// Complete the deferred relaunch that was waiting for pause to complete.
//等待暂停完成后relaunch Activity
prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
} else if (wasStopping) {
// We are also stopping, the stop request must have gone soon after the pause.
// We can't clobber it, because the stop confirmation will not be handled.
// We don't need to schedule another stop, we only need to let it happen.
//之前的状态为正在停止,将状态置回即可
prev.setState(STOPPING, "completePausedLocked");
} else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
// Clear out any deferred client hide we might currently have.
prev.setDeferHidingClient(false);
// If we were visible then resumeTopActivities will release resources before
// stopping.
//添加到stop列表中等待空闲时执行stop
prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
"completePauseLocked");
}
} else {
//App在pause过程中死亡
prev = null;
}
// It is possible the activity was freezing the screen before it was paused.
// In that case go ahead and remove the freeze this activity has on the screen
// since it is no longer visible.
if (prev != null) {
//停止屏幕冻结
prev.stopFreezingScreenLocked(true /*force*/);
}
//Activity暂停完毕
mPausingActivity = null;
}

//恢复前一个顶层Activity
if (resumeNext) {
final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();
if (topStack != null && !topStack.shouldSleepOrShutDownActivities()) {
mRootWindowContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
} else {
checkReadyForSleep();
final ActivityRecord top = topStack != null ? topStack.topRunningActivity() : null;
if (top == null || (prev != null && top != prev)) {
// If there are no more activities available to run, do resume anyway to start
// something. Also if the top activity on the stack is not the just paused
// activity, we need to go ahead and resume it to ensure we complete an
// in-flight app switch.
mRootWindowContainer.resumeFocusedStacksTopActivities();
}
}
}

if (prev != null) {
//恢复按键分发
prev.resumeKeyDispatchingLocked();
... //更新统计信息
}

//更新Activity可见性
mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);

// Notify when the task stack has changed, but only if visibilities changed (not just
// focus). Also if there is an active pinned stack - we always want to notify it about
// task stack changes, because its positioning may depend on it.
//通知Task状态发生变化
if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
|| (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
}
}

可以看到,无论暂停成功与否,最后都会走到ActivityRecord.completeFinishing方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* Complete activity finish request that was initiated earlier. If the activity is still
* pausing we will wait for it to complete its transition. If the activity that should appear in
* place of this one is not visible yet - we'll wait for it first. Otherwise - activity can be
* destroyed right away.
* @param reason Reason for finishing the activity.
* @return Flag indicating whether the activity was removed from history.
*/
ActivityRecord completeFinishing(String reason) {
... //状态检查

final boolean isCurrentVisible = mVisibleRequested || isState(PAUSED);
if (isCurrentVisible) {
... //更新Activity可见性
}

boolean activityRemoved = false;

// If this activity is currently visible, and the resumed activity is not yet visible, then
// hold off on finishing until the resumed one becomes visible.
// The activity that we are finishing may be over the lock screen. In this case, we do not
// want to consider activities that cannot be shown on the lock screen as running and should
// proceed with finishing the activity if there is no valid next top running activity.
// Note that if this finishing activity is floating task, we don't need to wait the
// next activity resume and can destroy it directly.
// TODO(b/137329632): find the next activity directly underneath this one, not just anywhere
final ActivityRecord next = getDisplayArea().topRunningActivity(
true /* considerKeyguardState */);
// isNextNotYetVisible is to check if the next activity is invisible, or it has been
// requested to be invisible but its windows haven't reported as invisible. If so, it
// implied that the current finishing activity should be added into stopping list rather
// than destroy immediately.
final boolean isNextNotYetVisible = next != null
&& (!next.nowVisible || !next.mVisibleRequested);

//如果此Activity当前可见,而要恢复的Activity还不可见,则推迟finish,直到要恢复的Activity可见为止
if (isCurrentVisible && isNextNotYetVisible) {
// Add this activity to the list of stopping activities. It will be processed and
// destroyed when the next activity reports idle.
//添加到stop列表中等待空闲时执行stop
addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
"completeFinishing");
//设置状态为stop中
setState(STOPPING, "completeFinishing");
} else if (addToFinishingAndWaitForIdle()) {
// We added this activity to the finishing list and something else is becoming resumed.
// The activity will complete finishing when the next activity reports idle. No need to
// do anything else here.
//将此Activity添加到待finish列表中,等待空闲时执行finish
} else {
// Not waiting for the next one to become visible, and nothing else will be resumed in
// place of this activity - requesting destruction right away.
//立刻销毁此Activity
activityRemoved = destroyIfPossible(reason);
}

return activityRemoved ? null : this;
}

对于非锁屏状态且当前要销毁的Activity在前台的情况下,该Activity可见而待恢复的Activity尚不可见,此时优先完成待恢复Activityresume生命周期,等到之后空闲再去处理待销毁Activitydestroy生命周期

所以在面试中常问的ActivityB返回到A的生命周期顺序我们从这里就可以看出来,理解后我们就不用去死记硬背了:

B.onPause -> A.onRestart -> A.onResume -> B.onStop -> B.onDestory

对于锁屏状态或者要销毁的Activity不在前台的情况下,由于不需要立刻恢复Activity,所以可能会直接处理待销毁Activitydestroy生命周期

我们以第一种当前要销毁的Activity在前台的情况分析,此时会将这个Activity添加到stop列表中,并将状态设置为STOPPING,之后返回到ActivityStack.completePauseLocked方法中,继续执行resumeNext工作

resumeNext中会调用RootWindowContainer.resumeFocusedStacksTopActivities方法恢复栈顶Activity,由于这个方法之前已经在
Android源码分析 - Activity启动流程(上) 中分析过了,这里就不再赘述了,我们还是将目光放在销毁流程上

通过之前的文章,我们知道恢复Activity会调用到ActivityThread.handleResumeActivity方法,而当Activity恢复完毕后,此方法最后一行会向MessageQueue添加一个IdleHandler,关于IdleHandler这里就不再介绍了,这是每位Android开发都应该了解的东西

1
2
3
4
5
6
7
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...
r.nextIdle = mNewActivities;
mNewActivities = r;
Looper.myQueue().addIdleHandler(new Idler());
}

这里的IdlerActivityThread的一个内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private class Idler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
ActivityClientRecord a = mNewActivities;
...
if (a != null) {
mNewActivities = null;
IActivityTaskManager am = ActivityTaskManager.getService();
ActivityClientRecord prev;
//遍历整条ActivityClientRecord.nextIdle链,依次调用ATMS.activityIdle
do {
if (a.activity != null && !a.activity.mFinished) {
try {
am.activityIdle(a.token, a.createdConfig, stopProfiling);
a.createdConfig = null;
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
prev = a;
a = a.nextIdle;
prev.nextIdle = null;
} while (a != null);
}
...
return false;
}
}

这里会遍历整个进程内所有的ActivityClientRecord,并依次调用ATMS.activityIdle方法

1
2
3
4
5
6
7
8
9
10
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
...
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
return;
}
mStackSupervisor.activityIdleInternal(r, false /* fromTimeout */,
false /* processPausingActivities */, config);
...
}

ActivityRecord.Token获取到ActivityRecord,接着调用ActivityStackSupervisor.activityIdleInternal方法

1
2
3
4
5
6
7
void activityIdleInternal(ActivityRecord r, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
...
// Atomically retrieve all of the other things to do.
processStoppingAndFinishingActivities(r, processPausingActivities, "idle");
...
}

这里我们只需要重点关注processStoppingAndFinishingActivities这一个方法,从方法名我们也能看出来,它是用来处理Activity stopdestroy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* Processes the activities to be stopped or destroyed. This should be called when the resumed
* activities are idle or drawn.
*/
private void processStoppingAndFinishingActivities(ActivityRecord launchedActivity,
boolean processPausingActivities, String reason) {
// Stop any activities that are scheduled to do so but have been waiting for the transition
// animation to finish.
ArrayList<ActivityRecord> readyToStopActivities = null;
for (int i = mStoppingActivities.size() - 1; i >= 0; --i) {
final ActivityRecord s = mStoppingActivities.get(i);
final boolean animating = s.isAnimating(TRANSITION | PARENTS,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
//不在动画中或者ATMS服务正在关闭
if (!animating || mService.mShuttingDown) {
//跳过正在pause的Activitiy
if (!processPausingActivities && s.isState(PAUSING)) {
// Defer processing pausing activities in this iteration and reschedule
// a delayed idle to reprocess it again
removeIdleTimeoutForActivity(launchedActivity);
scheduleIdleTimeout(launchedActivity);
continue;
}

if (readyToStopActivities == null) {
readyToStopActivities = new ArrayList<>();
}
//将准备好stop的Activitiy加入列表中
readyToStopActivities.add(s);

mStoppingActivities.remove(i);
}
}

final int numReadyStops = readyToStopActivities == null ? 0 : readyToStopActivities.size();
for (int i = 0; i < numReadyStops; i++) {
final ActivityRecord r = readyToStopActivities.get(i);
//Activity是否在任务栈中
if (r.isInHistory()) {
if (r.finishing) {
// TODO(b/137329632): Wait for idle of the right activity, not just any.
//被标记为finishing,尝试销毁Activity
r.destroyIfPossible(reason);
} else {
//否则仅仅只是stop Activity
r.stopIfPossible();
}
}
}

final int numFinishingActivities = mFinishingActivities.size();
if (numFinishingActivities == 0) {
return;
}

// Finish any activities that are scheduled to do so but have been waiting for the next one
// to start.
final ArrayList<ActivityRecord> finishingActivities = new ArrayList<>(mFinishingActivities);
mFinishingActivities.clear();
for (int i = 0; i < numFinishingActivities; i++) {
final ActivityRecord r = finishingActivities.get(i);
if (r.isInHistory()) {
//立刻执行Activity的销毁流程
r.destroyImmediately(true /* removeFromApp */, "finish-" + reason);
}
}
}

对于被标记为finishingActivity,调用destroyIfPossible方法销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* Destroy and cleanup the activity both on client and server if possible. If activity is the
* last one left on display with home stack and there is no other running activity - delay
* destroying it until the next one starts.
*/
boolean destroyIfPossible(String reason) {
//设置状态
setState(FINISHING, "destroyIfPossible");

// Make sure the record is cleaned out of other places.
//确保此Activity已从待stop列表中移除
mStackSupervisor.mStoppingActivities.remove(this);

final ActivityStack stack = getRootTask();
final TaskDisplayArea taskDisplayArea = getDisplayArea();
final ActivityRecord next = taskDisplayArea.topRunningActivity();
final boolean isLastStackOverEmptyHome =
next == null && stack.isFocusedStackOnDisplay()
&& taskDisplayArea.getOrCreateRootHomeTask() != null;
if (isLastStackOverEmptyHome) {
// Don't destroy activity immediately if this is the last activity on the display and
// the display contains home stack. Although there is no next activity at the moment,
// another home activity should be started later. Keep this activity alive until next
// home activity is resumed. This way the user won't see a temporary black screen.
//如果Home栈存在且这是当前焦点栈中最后一个Activity,则不要立即销毁它
//将此Activity添加到待finish列表中,等待空闲时执行finish
addToFinishingAndWaitForIdle();
return false;
}
//设置finishing标记(之前设过了,这里是重复设置)
makeFinishingLocked();

//立刻执行Activity的销毁流程
final boolean activityRemoved = destroyImmediately(true /* removeFromApp */,
"finish-imm:" + reason);

// If the display does not have running activity, the configuration may need to be
// updated for restoring original orientation of the display.
//更新可见性和屏幕显示方向
if (next == null) {
mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
//更新恢复栈顶Activity
if (activityRemoved) {
mRootWindowContainer.resumeFocusedStacksTopActivities();
}

return activityRemoved;
}

这里做了最后的一些判断,然后调用destroyImmediately方法,立刻执行Activity的销毁流程(这里和上一个方法processStoppingAndFinishingActivities中,待finish列表的处理是一样的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
* Destroy the current CLIENT SIDE instance of an activity. This may be called both when
* actually finishing an activity, or when performing a configuration switch where we destroy
* the current client-side object but then create a new client-side object for this same
* HistoryRecord.
* Normally the server-side record will be removed when the client reports back after
* destruction. If, however, at this point there is no client process attached, the record will
* be removed immediately.
*
* @return {@code true} if activity was immediately removed from history, {@code false}
* otherwise.
*/
boolean destroyImmediately(boolean removeFromApp, String reason) {
//已经被销毁或正在被销毁,直接返回
if (isState(DESTROYING, DESTROYED)) {
return false;
}

boolean removedFromHistory = false;

//清理工作
cleanUp(false /* cleanServices */, false /* setState */);

if (hasProcess()) {
//清理更新工作
if (removeFromApp) {
app.removeActivity(this);
if (!app.hasActivities()) {
mAtmService.clearHeavyWeightProcessIfEquals(app);
// Update any services we are bound to that might care about whether
// their client may have activities.
// No longer have activities, so update LRU list and oom adj.
//更新进程信息
app.updateProcessInfo(true /* updateServiceConnectionActivities */,
false /* activityChange */, true /* updateOomAdj */,
false /* addPendingTopUid */);
}
}

boolean skipDestroy = false;

try {
//调度销毁生命周期事务
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
DestroyActivityItem.obtain(finishing, configChangeFlags));
} catch (Exception e) {
// We can just ignore exceptions here... if the process has crashed, our death
// notification will clean things up.
if (finishing) {
//从历史任务中移除
removeFromHistory(reason + " exceptionInScheduleDestroy");
removedFromHistory = true;
skipDestroy = true;
}
}

nowVisible = false;

// If the activity is finishing, we need to wait on removing it from the list to give it
// a chance to do its cleanup. During that time it may make calls back with its token
// so we need to be able to find it on the list and so we don't want to remove it from
// the list yet. Otherwise, we can just immediately put it in the destroyed state since
// we are not removing it from the list.
if (finishing && !skipDestroy) {
//设置状态
setState(DESTROYING,
"destroyActivityLocked. finishing and not skipping destroy");
//设置销毁超时回调
mAtmService.mH.postDelayed(mDestroyTimeoutRunnable, DESTROY_TIMEOUT);
} else {
//设置状态
setState(DESTROYED,
"destroyActivityLocked. not finishing or skipping destroy");
app = null;
}
} else {
// Remove this record from the history.
if (finishing) {
//没有绑定进程,从历史任务中移除
removeFromHistory(reason + " hadNoApp");
removedFromHistory = true;
} else {
//没有绑定进程且不在finishing中,直接设置状态为已被销毁
setState(DESTROYED, "destroyActivityLocked. not finishing and had no app");
}
}

configChangeFlags = 0;

return removedFromHistory;
}

scheduleTransaction

这个方法做了一些清理工作,重头戏在于调用了ClientLifecycleManager.scheduleTransaction方法调度销毁生命周期事务,接下来我们就重点分析这个事务的执行路径

scheduleTransaction的调用链路我们在 Android源码分析 - Activity启动流程(下) 中已经分析过了,这里我就简单的标注一下流程:

ClientLifecycleManager.scheduleTransaction ->
ClientTransaction.schedule ->
ActivityThread.scheduleTransaction ->
ClientTransaction.preExecute ->
ActivityLifecycleItem.preExecute->
ActivityThread.sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction) ->
TransactionExecutor.execute ->
TransactionExecutor.executeCallbacks ->
TransactionExecutor.executeLifecycleState ->
TransactionExecutor.cycleToPath ->
ActivityLifecycleItem.execute ->
ActivityLifecycleItem.postExecute

这里的链路基本上和Activity启动事务链路相差无几,甚至更短了(Activity销毁事务没有添加callback),所以没看过我上篇文章的强烈推荐去看一下,这里我就不再做分析了

我们从TransactionExecutor.cycleToPath开始,之前我们分析过,我们在事务中设置的ActivityLifecycleItem代表了Activity最终需要到达执行的生命周期,而中间的那些过渡生命周期就由cycleToPath方法推进执行,我们目前的生命周期状态为ON_PAUSE,而我们的目标生命周期为ON_DESTROY,中间还夹着一个ON_STOP,所以这个方法会帮我们执行ClientTransactionHandler.handleStopActivity方法,也就是ActivityThread.handleStopActivity方法

handleStopActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void handleStopActivity(IBinder token, int configChanges,
PendingTransactionActions pendingActions, boolean finalStateRequest /* false */, String reason) {
final ActivityClientRecord r = mActivities.get(token);
r.activity.mConfigChangeFlags |= configChanges;

final StopInfo stopInfo = new StopInfo();
//执行onStop生命周期
performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest,
reason);

//更新可见性
updateVisibility(r, false);

// Make sure any pending writes are now committed.
//确保所有全局任务都被处理完成
if (!r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}

//记录Stop信息(不过在后续销毁链路中似乎并没有被用到)
stopInfo.setActivity(r);
stopInfo.setState(r.state);
stopInfo.setPersistentState(r.persistentState);
pendingActions.setStopInfo(stopInfo);
mSomeActivitiesChanged = true;
}

接下来的路径就和其他生命周期差不多了,大部分内容我都用注释标注了,大家顺着往下看就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Core implementation of stopping an activity.
* @param r Target activity client record.
* @param info Action that will report activity stop to server.
* @param saveState Flag indicating whether the activity state should be saved.
* @param finalStateRequest Flag indicating if this call is handling final lifecycle state
* request for a transaction.
* @param reason Reason for performing this operation.
*/
private void performStopActivityInner(ActivityClientRecord r, StopInfo info,
boolean saveState, boolean finalStateRequest /* false */, String reason) {
if (r != null) {
... //异常状态处理

// One must first be paused before stopped...
//如果没有被暂停则先执行pause生命周期
performPauseActivityIfNeeded(r, reason);

... //设置描述(Activity.onCreateDescription)

//回调Activity的onStop方法
callActivityOnStop(r, saveState, reason);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* Calls {@link Activity#onStop()} and {@link Activity#onSaveInstanceState(Bundle)}, and updates
* the client record's state.
* All calls to stop an activity must be done through this method to make sure that
* {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
*/
private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
// Before P onSaveInstanceState was called before onStop, starting with P it's
// called after. Before Honeycomb state was always saved before onPause.
//这里shouldSaveState为true,因为activity.mFinished早在performPauseActivity的时候就被设为了true
final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
&& !r.isPreHoneycomb();
//targetSdkVersion为Android P (Android 9)之前
final boolean isPreP = r.isPreP();
if (shouldSaveState && isPreP) {
callActivityOnSaveInstanceState(r);
}

try {
//执行stop生命周期
r.activity.performStop(r.mPreserveWindow, reason);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
...
}
//设置生命周期状态
r.setState(ON_STOP);

if (shouldSaveState && !isPreP) {
callActivityOnSaveInstanceState(r);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
final void performStop(boolean preserveWindow, String reason) {
mDoReportFullyDrawn = false;
//Loader相关,详见https://developer.android.com/guide/components/loaders
mFragments.doLoaderStop(mChangingConfigurations /*retain*/);

// Disallow entering picture-in-picture after the activity has been stopped
//stop后禁用画中画
mCanEnterPictureInPicture = false;

if (!mStopped) {
//分发PreStopped事件,执行所有注册的ActivityLifecycleCallbacks的onActivityPreStopped回调
dispatchActivityPreStopped();
//关闭所有子窗口
if (mWindow != null) {
mWindow.closeAllPanels();
}

// If we're preserving the window, don't setStoppedState to true, since we
// need the window started immediately again. Stopping the window will
// destroys hardware resources and causes flicker.
if (!preserveWindow && mToken != null && mParent == null) {
//设置停止状态,释放硬件资源,销毁Surface
WindowManagerGlobal.getInstance().setStoppedState(mToken, true);
}

//FragmentManager分发stop状态
mFragments.dispatchStop();

mCalled = false;
//执行onStop回调
mInstrumentation.callActivityOnStop(this);
EventLogTags.writeWmOnStopCalled(mIdent, getComponentName().getClassName(), reason);
if (!mCalled) {
//必须要调用super.onStop方法
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onStop()");
}

//释放Cursors
synchronized (mManagedCursors) {
final int N = mManagedCursors.size();
for (int i=0; i<N; i++) {
ManagedCursor mc = mManagedCursors.get(i);
if (!mc.mReleased) {
mc.mCursor.deactivate();
mc.mReleased = true;
}
}
}

mStopped = true;
//分发PostStopped事件,执行所有注册的ActivityLifecycleCallbacks的onActivityPostStopped回调
dispatchActivityPostStopped();
}
mResumed = false;
}

通过Instrumentation执行onStop回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected void onStop() {
//ActionBar动画
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
//共享元素动画
mActivityTransitionState.onStop();
//分发PostStopped事件,执行所有注册的ActivityLifecycleCallbacks的onActivityPostStopped回调
dispatchActivityStopped();
mTranslucentCallback = null;
//super.onStop标注为已被执行
mCalled = true;

... //自动填充相关

mEnterAnimationComplete = false;
}

这样,一整个onStop生命周期就执行完成了,最后还剩下个onDestroy,根据之前写的事务调度链路,现在应该走到了DestroyActivityItem.execute方法

1
2
3
4
5
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
client.handleDestroyActivity(token, mFinished, mConfigChanges,
false /* getNonConfigInstance */, "DestroyActivityItem");
}

可以看到,实际上就直接调用了ActivityThread.handleDestroyActivity方法

handleDestroyActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
//执行onDestroy生命周期
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance, reason);
if (r != null) {
//清理之前设置的延时移除的window
cleanUpPendingRemoveWindows(r, finishing);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
if (v != null) {
if (r.activity.mVisibleFromServer) {
mNumVisibleActivities--;
}
IBinder wtoken = v.getWindowToken();
if (r.activity.mWindowAdded) {
if (r.mPreserveWindow) {
// Hold off on removing this until the new activity's
// window is being added.
r.mPendingRemoveWindow = r.window;
r.mPendingRemoveWindowManager = wm;
// We can only keep the part of the view hierarchy that we control,
// everything else must be removed, because it might not be able to
// behave properly when activity is relaunching.
//从DecorView中移除ContentView
r.window.clearContentView();
} else {
//立刻执行View的移除操作,释放硬件资源,销毁Surface,回调View.onDetachedFromWindow
wm.removeViewImmediate(v);
}
}
if (wtoken != null && r.mPendingRemoveWindow == null) {
//移除指定Window下的所有rootView
WindowManagerGlobal.getInstance().closeAll(wtoken,
r.activity.getClass().getName(), "Activity");
} else if (r.mPendingRemoveWindow != null) {
// We're preserving only one window, others should be closed so app views
// will be detached before the final tear down. It should be done now because
// some components (e.g. WebView) rely on detach callbacks to perform receiver
// unregister and other cleanup.
//移除指定Window下除了当前DecorView以外的所有rootView
WindowManagerGlobal.getInstance().closeAllExceptView(token, v,
r.activity.getClass().getName(), "Activity");
}
r.activity.mDecor = null;
}
if (r.mPendingRemoveWindow == null) {
// If we are delaying the removal of the activity window, then
// we can't clean up all windows here. Note that we can't do
// so later either, which means any windows that aren't closed
// by the app will leak. Well we try to warning them a lot
// about leaking windows, because that is a bug, so if they are
// using this recreate facility then they get to live with leaks.
WindowManagerGlobal.getInstance().closeAll(token,
r.activity.getClass().getName(), "Activity");
}

// Mocked out contexts won't be participating in the normal
// process lifecycle, but if we're running with a proper
// ApplicationContext we need to have it tear down things
// cleanly.
//清理Context
Context c = r.activity.getBaseContext();
if (c instanceof ContextImpl) {
((ContextImpl) c).scheduleFinalCleanup(
r.activity.getClass().getName(), "Activity");
}
}
if (finishing) {
try {
//处理一些销毁后的事项,移除超时回调等
ActivityTaskManager.getService().activityDestroyed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
mSomeActivitiesChanged = true;
}

Window这块的处理我目前也不太了解,等以后我学习了WMS那块再补吧

其他的和之前的套路一样,调用performDestroyActivity方法执行销毁Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/** Core implementation of activity destroy call. */
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (r != null) {
activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
}

//如果没有被暂停则先执行pause生命周期
performPauseActivityIfNeeded(r, "destroy");

//如果没有被停职则先执行stop生命周期
if (!r.stopped) {
callActivityOnStop(r, false /* saveState */, "destroy");
}
if (getNonConfigInstance) {
... //getNonConfigInstance为false,这里不执行
}
try {
r.activity.mCalled = false;
//执行onDestroy回调
mInstrumentation.callActivityOnDestroy(r.activity);
//必须要调用super.onDestroy方法
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + safeToComponentShortString(r.intent) +
" did not call through to super.onDestroy()");
}
//关闭所有子窗口
if (r.window != null) {
r.window.closeAllPanels();
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
...
}
//设置生命周期状态
r.setState(ON_DESTROY);
}
//空闲时清理资源
schedulePurgeIdler();
// updatePendingActivityConfiguration() reads from mActivities to update
// ActivityClientRecord which runs in a different thread. Protect modifications to
// mActivities to avoid race.
synchronized (mResourcesManager) {
mActivities.remove(token);
}
//严格模式更新Activity计数器,与实际Activity数量对比,判断是否产生内存泄漏
StrictMode.decrementExpectedActivityCount(activityClass);
return r;
}

通过Instrumentation调用Activity.performDestroy方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
final void performDestroy() {
//分发PreDestroyed事件,执行所有注册的ActivityLifecycleCallbacks的onActivityPreDestroyed回调
dispatchActivityPreDestroyed();
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
onDestroy();
EventLogTags.writeWmOnDestroyCalled(mIdent, getComponentName().getClassName(),
"performDestroy");
mFragments.doLoaderDestroy();
if (mVoiceInteractor != null) {
mVoiceInteractor.detachActivity();
}
//分发PostDestroyed事件,执行所有注册的ActivityLifecycleCallbacks的onActivityPostDestroyed回调
dispatchActivityPostDestroyed();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
protected void onDestroy() {
//super.onDestroy标注为已被执行
mCalled = true;

... //自动填充相关

// dismiss any dialogs we are managing.
//关闭所有被管理的Dialog
if (mManagedDialogs != null) {
final int numDialogs = mManagedDialogs.size();
for (int i = 0; i < numDialogs; i++) {
final ManagedDialog md = mManagedDialogs.valueAt(i);
if (md.mDialog.isShowing()) {
md.mDialog.dismiss();
}
}
mManagedDialogs = null;
}

// close any cursors we are managing.
//关闭所有被管理的Cursor
synchronized (mManagedCursors) {
int numCursors = mManagedCursors.size();
for (int i = 0; i < numCursors; i++) {
ManagedCursor c = mManagedCursors.get(i);
if (c != null) {
c.mCursor.close();
}
}
mManagedCursors.clear();
}

// Close any open search dialog
//关闭系统搜索服务的弹窗
if (mSearchManager != null) {
mSearchManager.stopSearch();
}

if (mActionBar != null) {
mActionBar.onDestroy();
}

//分发Destroyed事件,执行所有注册的ActivityLifecycleCallbacks的onActivityDestroyed回调
dispatchActivityDestroyed();

//内容捕获服务
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
}

DestroyActivityItem没有重写postExecute方法,所以到此为止,Activity整个销毁流程就结束了

Tips

我们通过本篇文章的分析,可以发现,触发Activity销毁后,onStoponDestroy这两个生命周期回调的触发时机是不确定的,如果有需求需要在确定Activity要被销毁后立刻执行,我们可以在onPause回调中调用Activity.isFinishing方法判断mFinished标志是否被置true,如果为true则可以判定这个Activity将被销毁

结尾

至此,Activity的启动流程和销毁流程我们都分析完了,后面应该暂时不会再写Activity相关的源码分析了

之后的一段时间,我可能会将我的精力投入到AIGC的技术调研中,Android源码分析这一系列的后续更新可能会放慢,希望大家多多谅解