<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>opoojkk</title><link>https://opoojkk.com/</link><description>Recent content on opoojkk</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Thu, 30 Jul 2026 07:52:51 +0800</lastBuildDate><atom:link href="https://opoojkk.com/index.xml" rel="self" type="application/rss+xml"/><item><title>20260730</title><link>https://opoojkk.com/content/posts/20260730/</link><pubDate>Thu, 30 Jul 2026 07:52:51 +0800</pubDate><guid>https://opoojkk.com/content/posts/20260730/</guid><description/></item><item><title>最近的变化</title><link>https://opoojkk.com/posts/2026/07/30/20260730/</link><pubDate>Thu, 30 Jul 2026 07:52:51 +0800</pubDate><guid>https://opoojkk.com/posts/2026/07/30/20260730/</guid><description>&lt;p>上次写博客还是三月份，转眼到了7月底，这段时间有很多变化。&lt;/p>
&lt;h2 class="heading" id="裁员">
 裁员&lt;span class="heading__anchor"> &lt;a href="#%e8%a3%81%e5%91%98">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>时间线上最早是在四月，四月初被告诉裁员了，在上家公司待了9个月，拿到了两个月赔偿。前司业务越来越难，日常工作的体验也比较一般，被裁时暗爽了一把，也算脱离苦海了。&lt;/p>
&lt;p>出去玩加上回家大概有半个月的时间，四月中旬投了些北京的岗位，大多石沉大海，也有反馈不合适的，一下慌了。开始考虑怎么应付，用AI写了些东西，被现实暴击后继续找工作。&lt;/p>
&lt;p>开始的目标城市是成都，一方面是有姐姐一家定居成都，另一方面喜欢成都重庆的饮食。其他的城市同时也在看，成都某个岗位走到后边发现薪资相比其他城市少很多，社保公积金基数和比例都比较低。&lt;/p>
&lt;p>上一家在教培，考虑传统互联网业务受AI影响可能会比较大，因此期望能进入智能设备的领域，加上薪资、社保公积金、回家方便程度这些因素，综合考虑接受了杭州的机会。&lt;/p>
&lt;p>截止目前都还算的上满意。&lt;/p>
&lt;h2 class="heading" id="朋友">
 朋友&lt;span class="heading__anchor"> &lt;a href="#%e6%9c%8b%e5%8f%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在我被裁聊赔偿的同时，在北京的一个朋友也被裁员，她六月初选择了回老家。另一个跳槽去了昆明。这样原本在北京一起玩的6个人最后只有两个还留在北京（我到了杭州）。在北京生活实在算不上舒服，节奏快、气候干燥、花粉多容易过敏，经常被查身份证，其他像是行人和汽车抢路（谁快谁过得去）等等。一起玩的几个朋友都离开了，更不想在北京了。&lt;/p>
&lt;p>留在北京的主要原因是还有北京本地的女朋友，分手之后再也没什么原因了。&lt;/p>
&lt;p>分手的原因我认为是很多观念有冲突，越来越不舒服。在我看来关系应该是平等的，她觉着男生应该付出更多。分开都能更舒服些。&lt;/p>
&lt;h2 class="heading" id="搬家">
 搬家&lt;span class="heading__anchor"> &lt;a href="#%e6%90%ac%e5%ae%b6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>东西实在是多，提前来了一趟杭州租好了房子，然后回去北京收拾东西，往杭州寄。东西实在太多了，邮费花了大概有两千五。还有两只猫，把猫带过来是最麻烦的，尤其一个人要带两只，顺风车时间长、花费高，飞机要求严格，不能带两只，最后决定高铁。高铁要求有疫检证明，每只猫500，实际上也是托运，只不过是放到单独的小箱子里，前两天不怎么吃粮喝水有些担心，幸好都没什么事，到这边三四天开始疯跑了。开始的高铁因为遇到台风停运了，问过之后可以用不同车次的疫检证明（有效期内），免了一回折腾。&lt;/p>
&lt;p>幸好杭州的房租相比北京便宜很多，比北京主卧更少的房租，租到了一间Loft公寓，总算能自己住了，离公司也近，步行十几分钟。从北京要离开时很想买车，通勤离公司很近，没有必要，平时又没什么需要开车的，暂时放一放。&lt;/p>
&lt;h2 class="heading" id="后续">
 后续&lt;span class="heading__anchor"> &lt;a href="#%e5%90%8e%e7%bb%ad">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>因为工作的变动，健身、读书、学习几乎都停下来了，后续的计划就是恢复这些，杭州周边还有些朋友，找周末住个约下之类。&lt;/p>
&lt;p>写的都是些流水账，总体大致就是这样了。&lt;/p></description></item><item><title>Android 17 MessageQueue 无锁化实现解析</title><link>https://opoojkk.com/posts/2026/03/16/android-17-messagequeue-lock-free/</link><pubDate>Mon, 16 Mar 2026 08:31:03 +0800</pubDate><guid>https://opoojkk.com/posts/2026/03/16/android-17-messagequeue-lock-free/</guid><description>&lt;p>最近 Android 有一个比较重磅的变化 —— MessageQueue 的实现从原来的有锁结构改成了无锁结构。&lt;/p>
&lt;p>MessageQueue 是 Android 中非常核心的一套机制。
应用中的各种事件都会通过 MessageQueue 来进行排队和分发。&lt;/p>
&lt;p>旧的有锁实现，本质上是一个 按时间排序的单链表优先队列。&lt;/p>
&lt;p>这里的“优先”并不是传统意义上的 priority，而是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">when&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">越小&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">优先级越高&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>也就是说 谁应该更早执行，谁就排在前面。&lt;/p>
&lt;p>消息按照 &lt;code>when&lt;/code> 字段排序：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">mMessages&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">▼&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Message&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">when&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">10&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Message&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">when&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">20&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Message&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">when&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">50&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Message&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">when&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">100&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>因此队列始终保证：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">when&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">最小的消息在队头&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>MessageQueue.next()&lt;/code> 每次只需要取：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">mMessages&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>即可得到下一条要执行的消息。&lt;/p>
&lt;p>这里有一个非常容易被误解的地方：&lt;/p>
&lt;p>MessageQueue 并不是 FIFO 队列。&lt;/p>
&lt;p>很多人会认为消息是按照发送顺序执行的，但实际上 MessageQueue 是按照 &lt;code>when&lt;/code> 排序的。&lt;/p>
&lt;p>例如：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">handler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">sendMessageDelayed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msgA&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">5000&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">handler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">sendMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msgB&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>发送顺序是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">msgA&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">msgB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>但 &lt;code>when&lt;/code> 的值是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">msgA&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">5000&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">msgB&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>因此队列顺序变成：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">msgB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">msgA&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>也就是说：执行顺序由时间决定，而不是发送顺序。&lt;/p>
&lt;p>所以 MessageQueue 更准确的描述其实是：按执行时间排序的优先队列。&lt;/p>
&lt;p>旧实现中：&lt;/p>
&lt;p>无论是&lt;/p>
&lt;ul>
&lt;li>插入消息&lt;/li>
&lt;li>获取下一条消息&lt;/li>
&lt;/ul>
&lt;p>都需要先获取锁。&lt;/p>
&lt;p>基本流程如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">producer线程&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">▼&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">获取锁&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">▼&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">按&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">when&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">排序插入链表&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">▼&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">释放锁&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>消费消息时同样需要获取锁。&lt;/p>
&lt;p>当消息较多或者多个线程同时插入消息时，就容易出现锁竞争。&lt;/p>
&lt;p>同时 UI 渲染相关任务依赖 同步屏障（Sync Barrier）机制 来保证优先级。&lt;/p>
&lt;p>当 Choreographer 插入同步屏障后：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">同步消息被阻塞&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">异步消息继续执行&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>新的版本中，将消息处理拆成了两个阶段：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">插入消息&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">2&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">消费消息&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>整体结构也发生了变化。&lt;/p>
&lt;p>旧实现中生产者线程直接操作有序链表。&lt;/p>
&lt;p>而新的实现结构变成：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Producer&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">threads&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">▼&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Lock&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">free&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Stack&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">▼&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">drainStack&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">▼&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Priority&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Queue&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>也就是说：&lt;/p>
&lt;ol>
&lt;li>生产者线程&lt;/li>
&lt;li>只负责把消息压入无锁栈&lt;/li>
&lt;/ol>
&lt;p>真正的排序操作只在 Looper 线程消费消息时 才发生。&lt;/p>
&lt;h2 class="heading" id="分发">
 分发&lt;span class="heading__anchor"> &lt;a href="#%e5%88%86%e5%8f%91">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在真正开始分发消息时，Looper 线程会先调用：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">drainStack&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>把无锁栈中的消息转移到优先队列中。&lt;/p>
&lt;p>源码如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">drainStack&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">StackNode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">oldTop&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">while&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">oldTop&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isMessageNode&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">MessageNode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">oldTopMessageNode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MessageNode&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">oldTop&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">oldTopMessageNode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">removeFromStack&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">insertIntoPriorityQueue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">oldTopMessageNode&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">MessageNode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">inserted&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">oldTopMessageNode&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">oldTop&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">oldTopMessageNode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mNext&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">inserted&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mNext&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里做的事情其实很简单：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">stack
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">priority queue
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>完整执行流程如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">Handler.sendMessage
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 构造 MessageNode
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CAS 压栈
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">┌──────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Lock-free Stack │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└──────────┬───────────┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> drainStack()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">┌─────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Priority Queue │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Sync Queue Async Queue│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└──────────┬──────────────┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 选择最早可执行消息
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">dispatchMessage()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">handleMessage()
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>之所以要设计这一层 stack，主要是为了 降低多线程插入消息时的竞争。&lt;/p>
&lt;p>如果生产者线程直接操作优先队列，那么每次插入都会涉及：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">heap&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">调整&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nf">O&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>同时还需要锁保护。&lt;/p>
&lt;p>而 stack 的插入是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">O&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>并且只需要一次 CAS。&lt;/p>
&lt;h2 class="heading" id="三种状态">
 三种状态&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%89%e7%a7%8d%e7%8a%b6%e6%80%81">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在新的 MessageQueue 中，涉及到三种状态节点：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">STACK_NODE_ACTIVE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">STACK_NODE_PARKED&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">STACK_NODE_TIMEDPARK&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>分别表示：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ACTIVE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Looper线程正在运行&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">PARKED&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Looper线程阻塞等待消息&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">TIMEDPARK&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Looper线程正在等待某个时间点&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>例如：&lt;/p>
&lt;p>如果下一条消息要在 5 秒后执行，Looper 会进行类似：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">pollOnce&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">timeout&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>的定时等待。&lt;/p>
&lt;p>这三种状态会在 下一次插入消息时参与决策。&lt;/p>
&lt;p>新的 MessageQueue 内部维护了两个优先队列：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">同步消息队列&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">异步消息队列&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当存在同步屏障时：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">同步消息被阻塞&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">异步消息继续执行&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如果没有同步屏障，则会从两个队列中选择when 最早的消息执行。&lt;/p>
&lt;h2 class="heading" id="插入消息">
 插入消息&lt;span class="heading__anchor"> &lt;a href="#%e6%8f%92%e5%85%a5%e6%b6%88%e6%81%af">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>说完消费流程之后，再来看消息是如何加入队列的。&lt;/p>
&lt;p>在新的实现中，消息首先会加入一个 无锁栈。&lt;/p>
&lt;p>这个栈实际上是一个用链表实现的 Treiber Stack。&lt;/p>
&lt;p>插入逻辑如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="k">while&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">StackNode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">old&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">StackNode&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getVolatile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mNext&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">old&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">compareAndSet&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">old&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>CAS 的含义是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">compareAndSet&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">expected&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">newValue&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>只有当当前值等于 expected 时才会更新成功，否则更新失败。&lt;/p>
&lt;p>三种状态在插入消息时的作用，就是 决定是否需要唤醒 Looper 线程。&lt;/p>
&lt;h3 class="heading" id="active">
 ACTIVE&lt;span class="heading__anchor"> &lt;a href="#active">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Looper线程正在运行&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>此时只需要压栈即可，不需要唤醒。&lt;/p>
&lt;h3 class="heading" id="parked">
 PARKED&lt;span class="heading__anchor"> &lt;a href="#parked">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Looper线程阻塞等待消息&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>此时插入新消息必须唤醒：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">nativeWake&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mPtr&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="timedpark">
 TIMEDPARK&lt;span class="heading__anchor"> &lt;a href="#timedpark">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Looper线程正在等待某个时间点&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>此时是否唤醒取决于：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">新消息执行时间&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">是否早于&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">当前唤醒时间&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如果更早，就需要提前唤醒。&lt;/p>
&lt;p>如果当前栈顶不是状态节点，而是 MessageNode，说明栈中已经堆积了消息。&lt;/p>
&lt;p>这时需要通过：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">mBottomOfStack&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>找到栈底的状态节点。&lt;/p>
&lt;p>结构类似：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">MessageNode&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">MessageNode&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">MessageNode&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="err">│&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">StateNode&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这样 &lt;code>sState&lt;/code> 既表示：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">栈顶节点&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">同时也包含栈状态&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如果状态和结构分开维护，就需要：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">CAS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stack&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">CAS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>两次原子操作。&lt;/p>
&lt;p>现在只需要：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">CAS&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sState&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>一次即可保证一致性。&lt;/p>
&lt;h1 class="heading" id="两个队列有什么不同">
 两个队列有什么不同&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%a4%e4%b8%aa%e9%98%9f%e5%88%97%e6%9c%89%e4%bb%80%e4%b9%88%e4%b8%8d%e5%90%8c">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>两个优先队列分别是：&lt;/p>
&lt;ul>
&lt;li>mPriorityQueue&lt;/li>
&lt;li>mAsyncPriorityQueue&lt;/li>
&lt;/ul>
&lt;p>插入逻辑如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msgNode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isAsync&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mAsyncPriorityQueue&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msgNode&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mPriorityQueue&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msgNode&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>同步消息进入同步队列，异步消息进入异步队列。&lt;/p>
&lt;p>这样在存在同步屏障时，可以直接从异步队列取消息，而不需要扫描整个队列。&lt;/p>
&lt;h2 class="heading" id="状态什么时候恢复-active">
 状态什么时候恢复 Active&lt;span class="heading__anchor"> &lt;a href="#%e7%8a%b6%e6%80%81%e4%bb%80%e4%b9%88%e6%97%b6%e5%80%99%e6%81%a2%e5%a4%8d-active">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>状态恢复为 &lt;code>ACTIVE&lt;/code> 的情况主要有：&lt;/p>
&lt;ol>
&lt;li>Looper线程被唤醒&lt;/li>
&lt;li>Looper进入 next() 开始处理消息&lt;/li>
&lt;/ol>
&lt;p>线程重新开始执行消息循环时，会恢复为 ACTIVE 状态。&lt;/p>
&lt;h2 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Android 17 对 MessageQueue 做了一次重要的重构。&lt;/p>
&lt;p>旧实现结构：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">单链表&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">全局锁&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">多线程竞争&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>新实现结构：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Lock&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">free&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Stack&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Priority&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Queue&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>生产者线程只需要：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">CAS&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">压栈&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>消费者线程再统一整理顺序。&lt;/p>
&lt;p>同时通过三种状态：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ACTIVE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">PARKED&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">TIMEDPARK&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>精细控制线程唤醒。&lt;/p>
&lt;p>整体思路可以总结为：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">无锁收集消息&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">单线程整理顺序&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">按需唤醒&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Looper&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这样既降低了锁竞争，也减少了不必要的线程唤醒。&lt;/p></description></item><item><title>Android 启动 Activity 的多种方式</title><link>https://opoojkk.com/posts/2026/02/01/android-activity-starting-strategies/</link><pubDate>Sun, 01 Feb 2026 18:11:03 +0800</pubDate><guid>https://opoojkk.com/posts/2026/02/01/android-activity-starting-strategies/</guid><description>&lt;p>我们知道，Activity 是 Android 界面层的核心承载形式，几乎所有用户可感知的内容，最终都会落在 Activity 上。所有的内容都是基于Activity的，无论是内容、弹窗，随着官方进一步收紧安全策略，除系统应用外，普通应用更难仅仅依靠无页面的Service形式长期存在。Activity之于Android的重要性自然不言而喻。&lt;/p>
&lt;p>那么，Activity是怎么启动的呢？&lt;/p>
&lt;h2 class="heading" id="原生方式">
 原生方式&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e7%94%9f%e6%96%b9%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>原生方式我们通常分成两种：显示启动和隐式启动。&lt;/p>
&lt;h3 class="heading" id="显示启动">
 显示启动&lt;span class="heading__anchor"> &lt;a href="#%e6%98%be%e7%a4%ba%e5%90%af%e5%8a%a8">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>显示启动即需要知道要启动的Activity是哪个，是最最常用的当属官方提供的方式，写起来是这样：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">intent&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">Intent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">DetailActivity&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="k">class&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当需要携带数据或是设置flag时，无非是多写两行代码，就变成了这样：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">intent&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">Intent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">DetailActivity&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="k">class&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">apply&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 携带数据
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">putExtra&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;小明&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 设置flag
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">addFlags&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">Intent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">FLAG_ACTIVITY_NEW_TASK&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这种方式非常灵活，每次启动添加参数、flag非常方便，编译器就能知道启动的Activity是否存在。
除了出现过的这些，可以使用的还有设置luanchMode、添加动画甚至是权限，也可以使用&lt;code>ActivityResultLauncher&lt;/code>接受新Activity的结果（startActivityForResult被弃用，不推荐使用）。&lt;/p>
&lt;p>如果全都好，也就不会有这篇文章了对吧。它的不足是很繁琐，即使是按照相同的方式启动一个带有相同参数的Activity也要再写一遍，违反了DRY（Don&amp;rsquo;t Repeat Yourself）原则。&lt;/p>
&lt;h3 class="heading" id="隐式启动">
 隐式启动&lt;span class="heading__anchor"> &lt;a href="#%e9%9a%90%e5%bc%8f%e5%90%af%e5%8a%a8">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>隐式启动和显示启动对应，也就是不知道要启动哪个具体Activity，只关心有什么特点，使用场景通常有几种：&lt;/p>
&lt;ol>
&lt;li>基础功能Activity&lt;/li>
&lt;li>外部Activity（三方登录）&lt;/li>
&lt;li>DeepLink（Scheme）&lt;/li>
&lt;/ol>
&lt;p>好处是调用方不需要关心启动的是Activity名称，携带必要的参数即可。略有的不足是编译期无法校验 Intent 是否能被正确解析，只有在运行时由系统进行匹配，问题往往要到执行阶段才能暴露。&lt;/p>
&lt;p>从H5中执行切携带参数时可能存在参数编码问题，需要编码，可以使用&lt;code>Uri.parse&lt;/code>或是&lt;code>Uri.appendQueryParameter&lt;/code>方法&lt;/p>
&lt;h2 class="heading" id="封装">
 封装&lt;span class="heading__anchor"> &lt;a href="#%e5%b0%81%e8%a3%85">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="工厂方法">
 工厂方法&lt;span class="heading__anchor"> &lt;a href="#%e5%b7%a5%e5%8e%82%e6%96%b9%e6%b3%95">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>封装中用到最多的当属工厂方法。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">DetailActivity&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">AppCompatActivity&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">companion&lt;/span> &lt;span class="k">object&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fun&lt;/span> &lt;span class="nf">start&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Long&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startActivity&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Intent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">DetailActivity&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="k">class&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">apply&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">putExtra&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里是start，也可能是launch等等，将一个Activity启动的方法收敛到一个方法中，好处是很容易找到哪里启动，不足是通常不能满足所有情况，Kotlin中支持默认参数使用起来稍微些，Java中更严重，当需要不同参数情况很多时，往往要定义很多个相同名称的方法满足业务需要，看上去很乱。另外，一旦在 start 方法中掺杂业务判断，Activity 很容易从“页面”变成承载逻辑的入口，反而增加维护成本。&lt;/p>
&lt;p>虽然封装了，使用时也一样是需要有所在模块的引入才能使用。&lt;/p>
&lt;h3 class="heading" id="dsl--builder">
 DSL / Builder&lt;span class="heading__anchor"> &lt;a href="#dsl--builder">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>这是我个人最喜欢的方式，利用Kotlin的扩展方法和reified关键字写出类似模板的代码。&lt;/p>
&lt;p>写法像是这样：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 不设置flag
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startActivity&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">DetailActivity&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">long&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 设置flag
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startActivity&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">DetailActivity&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">options&lt;/span>&lt;span class="p">={&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">flag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">Intent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">FLAG_ACTIVITY_NEW_TASK&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span>&lt;span class="n">extra&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">long&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>本质上只是封装了显示启动，写法更优雅，其他方面并无差异。一样支持编译器检查，一样需要依赖Activity所在模块。&lt;/p>
&lt;p>完整定义可以参考我的Gist：&lt;a href="https://gist.github.com/opoojkk/ad04f50d352855618190f35e2fef9e4f">opoojkk/ActivityKt.kt&lt;/a>&lt;/p>
&lt;h2 class="heading" id="三方库">
 三方库&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%89%e6%96%b9%e5%ba%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>当然可以说，本质上还是封装显示启动，不过是程度不同而已，当然是这样，要是这么说，就不存在其他的方式了，毕竟Android只支持显示启动和隐式启动🤷‍♀️。&lt;/p>
&lt;p>拿使用最多的框架ARouter举例，启动上的确是封装了显示启动，写法上倒是有几分像隐式启动中的DeepLink：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="nc">ARouter&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getInstance&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/picture/detail&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">withLong&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">123&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">navigation&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>好处是写法上足够简单清晰，一目了然，带了哪些参数路径是什么，更重要的是不需要与要启动的Activity所在的模块有耦合，只关心启动，由框架完成注册。也因此支持在组件化工程中使用。
不足嘛，硬要挑还是不能在编译器检查，传入的路径仍然是字符串。如果支持了，恐怕也没办法支持组件化了。&lt;/p>
&lt;h2 class="heading" id="对比">
 对比&lt;span class="heading__anchor"> &lt;a href="#%e5%af%b9%e6%af%94">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>方式&lt;/th>
 &lt;th>编译期校验&lt;/th>
 &lt;th>参数集中管理&lt;/th>
 &lt;th>使用成本&lt;/th>
 &lt;th>组件化友好&lt;/th>
 &lt;th>灵活性&lt;/th>
 &lt;th>适用场景&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>原生显示启动&lt;/td>
 &lt;td>有&lt;/td>
 &lt;td>无&lt;/td>
 &lt;td>低&lt;/td>
 &lt;td>无&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>小型项目、简单页面跳转&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>原生隐式启动&lt;/td>
 &lt;td>无&lt;/td>
 &lt;td>无&lt;/td>
 &lt;td>中&lt;/td>
 &lt;td>低&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>系统能力、三方应用、DeepLink&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>工厂方法&lt;/td>
 &lt;td>有&lt;/td>
 &lt;td>低&lt;/td>
 &lt;td>中&lt;/td>
 &lt;td>无&lt;/td>
 &lt;td>中&lt;/td>
 &lt;td>参数固定、跳转规则稳定的页面&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>DSL / Builder&lt;/td>
 &lt;td>有&lt;/td>
 &lt;td>中&lt;/td>
 &lt;td>中&lt;/td>
 &lt;td>无&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>Kotlin 项目、追求代码整洁度&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>ARouter 等三方库&lt;/td>
 &lt;td>无&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>组件化、多模块、大型工程&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;blockquote>
&lt;p>说明：&lt;/p>
&lt;ul>
&lt;li>“编译期校验”指是否能在编译阶段发现 Activity 不存在或参数错误&lt;/li>
&lt;/ul>
&lt;/blockquote></description></item><item><title>Vue录音模块状态机重构笔记</title><link>https://opoojkk.com/posts/2026/01/12/vue-recording-state-machine-refactoring-notes/</link><pubDate>Mon, 12 Jan 2026 22:13:42 +0800</pubDate><guid>https://opoojkk.com/posts/2026/01/12/vue-recording-state-machine-refactoring-notes/</guid><description>&lt;p>最近被抓去写微信小程序，按照决策者的话说，逻辑都是一样的。&lt;/p>
&lt;p>这句话对了一半。&lt;/p>
&lt;p>作为一个成熟的 UI 框架，支持的交互能力一定是相似的：点击、长按、滑动、双击，不论是 Android、iOS 还是小程序，最终都会落到同一套用户行为模型上。但另一半恰恰是问题所在——语言和范式，会反过来塑造你组织逻辑和状态的方式。&lt;/p>
&lt;p>以 Android 和小程序为例。Android 使用 Java、Kotlin 甚至 C++，这些都是强类型语言，有继承、多态、接口，重复逻辑既可以组合，也可以通过继承收敛。状态往往会被包进类里，由类型系统帮你兜底。&lt;/p>
&lt;p>而在 Vue / 小程序中，语言是 JavaScript，本身动态类型语言，在不引入 TypeScript 的情况下，几乎没有编译期类型约束；虽然语法上存在 class，但在实际工程中，很少通过继承体系来承载状态语义。&lt;/p>
&lt;p>这在简单页面里问题不大，但一旦交互复杂，很容易失控。&lt;/p>
&lt;p>这篇文章记录的，就是我在一个录音页面里，从“多个状态变量维护”一路踩坑，最终重构成状态机的过程。&lt;/p>
&lt;h2 class="heading" id="背景">
 背景&lt;span class="heading__anchor"> &lt;a href="#%e8%83%8c%e6%99%af">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>决定重构并不是为了“写得更优雅”，而是代码已经修不动了。&lt;/p>
&lt;p>页面里有两个主要流程：&lt;/p>
&lt;ul>
&lt;li>AI 提问流程：视频播放到指定时间 → 系统引导 → 用户长按录音 → 识别 → 反馈&lt;/li>
&lt;li>学生主动提问流程：用户点击 → 引导 → 长按录音 → 最多两轮对话 → 结束&lt;/li>
&lt;/ul>
&lt;p>限制条件很多：&lt;/p>
&lt;ul>
&lt;li>播放音频时不能提问或录音&lt;/li>
&lt;li>录音按钮和提问按钮是同一个组件&lt;/li>
&lt;li>长按开始，松手结束，上滑取消&lt;/li>
&lt;li>问题只会在体验版（类似 Android 的正式包）里完整暴露&lt;/li>
&lt;/ul>
&lt;p>真正卡住我的是一个 bug：&lt;/p>
&lt;p>长按后立刻松手，重复几次后，会进入“再也无法录音”的状态。&lt;/p>
&lt;p>日志看不出异常，复现不稳定，调了大半天没有头绪。越查越发现一个问题：
我已经不知道系统当前到底处在什么状态了。&lt;/p>
&lt;h3 class="heading" id="状态失控">
 状态失控&lt;span class="heading__anchor"> &lt;a href="#%e7%8a%b6%e6%80%81%e5%a4%b1%e6%8e%a7">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>先看状态变量。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">isRecordingCanceled&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">isPressingRecordButton&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">hasSlideToCancel&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">recordingReady&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">isWaitingForInput&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">isPlayingAudio&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">showRecordingUI&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">recordingStartTime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">audioFilePath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">recordingType&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">userActionCount&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">currentSessionCount&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">currentResponseCount&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">canTriggerAction&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 名义上的“主状态”
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">currentStatus&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">STATUS_WELCOME&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>变量本身都很合理，问题在于：
它们组合在一起，才代表一个“真实状态”，但这种组合完全靠人脑维护。&lt;/p>
&lt;p>判断“现在能不能开始录音”，需要这样写：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">isPressingRecordButton&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">!&lt;/span>&lt;span class="nx">isRecordingCanceled&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">recordingReady&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">!&lt;/span>&lt;span class="nx">isWaitingForInput&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">!&lt;/span>&lt;span class="nx">isPlayingAudio&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">showRecordingUI&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">currentStatus&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">STATUS_WAITING_INPUT&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 可能可以录音
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当这些变量组合在一起，可能性已经完全超过了可以用逻辑理清的范畴&lt;/p>
&lt;h3 class="heading" id="异步时序">
 异步时序&lt;span class="heading__anchor"> &lt;a href="#%e5%bc%82%e6%ad%a5%e6%97%b6%e5%ba%8f">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>微信的录音 API 是异步的：&lt;/p>
&lt;ul>
&lt;li>&lt;code>start()&lt;/code> 不代表录音已经开始&lt;/li>
&lt;li>硬件真正就绪后，才会触发 &lt;code>onStart&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>正常流程是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">按下 → start → onStart → 松手 → stop → onStop
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>但如果用户“秒按”：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">按下 → start → 松手 → stop → onStart → onStop
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>旧代码里的 &lt;code>onStart&lt;/code> 并不知道用户已经松手：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">recorderManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onStart&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">updateRecordingStatus&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>于是 UI 会被切到“录音中”，而用户此时已经放开手，状态就此错位。&lt;/p>
&lt;p>这正是那个“松手了还在录音”的根源。&lt;/p>
&lt;h3 class="heading" id="重构一明确状态">
 重构一：明确状态&lt;span class="heading__anchor"> &lt;a href="#%e9%87%8d%e6%9e%84%e4%b8%80%e6%98%8e%e7%a1%ae%e7%8a%b6%e6%80%81">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>第一步是把“隐含状态”变成“显式状态”。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">STATUS_SYSTEM_WAITING_INPUT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">12&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">STATUS_SYSTEM_RECORDING&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">13&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">STATUS_SYSTEM_PROCESSING&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">14&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">STATUS_USER_WAITING_INPUT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">22&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">STATUS_USER_RECORDING&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">23&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">STATUS_USER_PROCESSING&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">24&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>现在判断“是否可以录音”只需要一行：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">currentStatus&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">STATUS_SYSTEM_WAITING_INPUT&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>状态本身就是语义，不再需要拼布尔值。&lt;/p>
&lt;h3 class="heading" id="重构二用会话-id-隔离异步回调">
 重构二：用会话 ID 隔离异步回调&lt;span class="heading__anchor"> &lt;a href="#%e9%87%8d%e6%9e%84%e4%ba%8c%e7%94%a8%e4%bc%9a%e8%af%9d-id-%e9%9a%94%e7%a6%bb%e5%bc%82%e6%ad%a5%e5%9b%9e%e8%b0%83">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>为了解决时序问题，我引入了录音会话 ID。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">currentRecordSession&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">isHardwareRunning&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>开始录音时生成会话：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">session&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">now&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">currentRecordSession&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">session&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">recorderManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">start&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在 &lt;code>onStart&lt;/code> 中校验：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">currentRecordSession&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">recorderManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stop&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如果用户在硬件启动前松手，直接作废会话：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">currentRecordSession&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>所有异步回调都必须验证 session 是否有效，Race Condition 被彻底隔离。&lt;/p>
&lt;h3 class="heading" id="重构三状态驱动ui">
 重构三：状态驱动UI&lt;span class="heading__anchor"> &lt;a href="#%e9%87%8d%e6%9e%84%e4%b8%89%e7%8a%b6%e6%80%81%e9%a9%b1%e5%8a%a8ui">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>所有 UI 行为统一收敛到配置表：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">STATUS_CONFIG_MAP&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">STATUS_SYSTEM_WAITING_INPUT&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">buttonType&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;record&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">buttonEnabled&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">videoControlEnabled&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">STATUS_SYSTEM_PROCESSING&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">buttonEnabled&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>状态切换统一通过：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">toStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">newStatus&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>UI 不关心“为什么”，只关心“现在是什么状态”。&lt;/p>
&lt;h3 class="heading" id="重构四状态日志面板">
 重构四：状态日志面板&lt;span class="heading__anchor"> &lt;a href="#%e9%87%8d%e6%9e%84%e5%9b%9b%e7%8a%b6%e6%80%81%e6%97%a5%e5%bf%97%e9%9d%a2%e6%9d%bf">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>为了验证状态流是否正确，我在页面里加了一个状态日志面板，记录每一次状态切换和关键日志。&lt;/p>
&lt;p>事实证明这是非常值得的投入：
很多“偶现问题”，在日志里其实一眼就能看出来，只是以前没有工具帮你看见。&lt;/p>
&lt;h2 class="heading" id="重构结果">
 重构结果&lt;span class="heading__anchor"> &lt;a href="#%e9%87%8d%e6%9e%84%e7%bb%93%e6%9e%9c">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>秒按、长按、取消不再互相污染状态&lt;/li>
&lt;li>状态判断全部集中，逻辑路径清晰&lt;/li>
&lt;li>错误恢复不再依赖“记得重置哪些变量”&lt;/li>
&lt;/ul>
&lt;p>判断当前的状态由十几个变量改为一个变量，通过判断变量状态的值就能够判断当前显示的样式。好处很明显，降低了复杂度（想不到写代码还会用到这个词），只要流程是清晰的，状态的改变一定是按照预先考虑的顺序的，即使中间有状态的转换。&lt;/p>
&lt;p>当控件由多个布尔值维护时，其实已经意识到这部分代码很可能已经失控了， 变量越用越多，状态就越来越复杂，总有复杂到理不清楚的状态。&lt;/p>
&lt;h2 class="heading" id="反思">
 反思&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%8d%e6%80%9d">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>回过头再想，重构做的好吗，是还能做的更好的，这明显就是状态模式，可惜的是 JavaScript 在语言层面并不支持 sealed 这类类型系统能力，只能通过约定和运行时校验来模拟。&lt;/p>
&lt;p>回过头来看这个录音模块，可以先抽象出两个高优先级维度，再衍伸出具体业务状态。&lt;/p>
&lt;p>第一类：交互方式维度&lt;/p>
&lt;ul>
&lt;li>点击触发（Tap）&lt;/li>
&lt;li>长按触发（Long Press）&lt;/li>
&lt;/ul>
&lt;p>第二类：是否允许触发&lt;/p>
&lt;ul>
&lt;li>可触发&lt;/li>
&lt;li>不可触发（被音频播放、处理中、次数限制等条件锁住）&lt;/li>
&lt;/ul>
&lt;p>在这两个维度之下，才进一步派生出更具体的业务状态，比如：&lt;/p>
&lt;ul>
&lt;li>等待输入&lt;/li>
&lt;li>正在录音&lt;/li>
&lt;li>系统处理中&lt;/li>
&lt;li>等待退出&lt;/li>
&lt;li>奖励展示&lt;/li>
&lt;/ul>
&lt;p>在 JavaScript / Vue 里，这些关系通常会被压扁成一堆布尔值；
而在 Kotlin 里，这是密封类（sealed class）绝佳的使用场景。&lt;/p>
&lt;h3 class="heading" id="密封类描述状态">
 密封类描述状态&lt;span class="heading__anchor"> &lt;a href="#%e5%af%86%e5%b0%81%e7%b1%bb%e6%8f%8f%e8%bf%b0%e7%8a%b6%e6%80%81">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>如果这是 Android，我大概率会先定义一个状态模型，而不是一堆 flag。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">sealed&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">RecordState&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 点击类交互
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">sealed&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">Tap&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">RecordState&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">object&lt;/span> &lt;span class="nc">Disabled&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Tap&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">object&lt;/span> &lt;span class="nc">Ready&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Tap&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">object&lt;/span> &lt;span class="nc">Processing&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Tap&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 长按类交互
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">sealed&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">LongPress&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">RecordState&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">object&lt;/span> &lt;span class="nc">Disabled&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">LongPress&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">object&lt;/span> &lt;span class="nc">Waiting&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">LongPress&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">data&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">Recording&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">startTime&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Long&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">LongPress&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里有几个关键点：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>状态是封闭集合&lt;/p>
&lt;ul>
&lt;li>不存在“忘记处理的状态”&lt;/li>
&lt;li>编译器会强制你在 &lt;code>when&lt;/code> 里覆盖完整&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>交互方式本身是一级语义&lt;/p>
&lt;ul>
&lt;li>Tap 和 LongPress 是两棵完全独立的状态树&lt;/li>
&lt;li>不需要靠 &lt;code>isLongPress &amp;amp;&amp;amp; canRecord &amp;amp;&amp;amp; !isPlaying&lt;/code> 这种组合判断&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>是否可触发被状态本身表达&lt;/p>
&lt;ul>
&lt;li>&lt;code>Disabled&lt;/code> 就是不允许&lt;/li>
&lt;li>不需要额外的 &lt;code>canTrigger&lt;/code> 变量&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 class="heading" id="状态驱动-ui">
 状态驱动 UI&lt;span class="heading__anchor"> &lt;a href="#%e7%8a%b6%e6%80%81%e9%a9%b1%e5%8a%a8-ui">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>使用会非常简单，比如在 UI 层：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">when&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">val&lt;/span> &lt;span class="py">state&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">recordState&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">is&lt;/span> &lt;span class="nc">RecordState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Tap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Disabled&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">disableButton&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">is&lt;/span> &lt;span class="nc">RecordState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Tap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Ready&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">showTapButton&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">is&lt;/span> &lt;span class="nc">RecordState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Tap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Processing&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">showLoading&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">is&lt;/span> &lt;span class="nc">RecordState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">LongPress&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Disabled&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">disableRecordButton&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">is&lt;/span> &lt;span class="nc">RecordState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">LongPress&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Waiting&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">showRecordHint&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">is&lt;/span> &lt;span class="nc">RecordState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">LongPress&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Recording&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">showRecordingUI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startTime&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里没有任何“隐式约定”，每一种 UI 行为，都由一个确定的状态类型驱动。&lt;/p>
&lt;p>这次最难处理的，其实不是状态本身，而是录音 start / stop 的异步时序。&lt;/p>
&lt;p>在 Kotlin 中，我很可能会把它直接建模进状态里：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">data&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">Recording&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">sessionId&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Long&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">startTime&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Long&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">LongPress&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>然后在回调里只做一件事：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">currentState&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">Recording&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">currentState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sessionId&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">callbackSessionId&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 合法回调
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>非法回调在类型层面就就被过滤掉了，用不到依赖其他变量或是逻辑。&lt;/p>
&lt;h3 class="heading" id="对比">
 对比&lt;span class="heading__anchor"> &lt;a href="#%e5%af%b9%e6%af%94">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>在 Vue / JavaScript 里，我只能通过：&lt;/p>
&lt;ul>
&lt;li>手动维护 &lt;code>currentRecordSession&lt;/code>&lt;/li>
&lt;li>手动判断 &lt;code>isHardwareRunning&lt;/code>&lt;/li>
&lt;li>手动约定哪些状态允许哪些行为&lt;/li>
&lt;/ul>
&lt;p>才能达到类似的效果。&lt;/p>
&lt;p>我认为这是语言的定位不同造成的，JS最最开始也是只操作dom树的语言，至于后续为什么演变成现在的状态，我没有经历过，不敢轻下妄言。&lt;/p>
&lt;p>这次在小程序里用状态机，本质上是模拟 Kotlin sealed class ：&lt;/p>
&lt;ul>
&lt;li>状态是有限的&lt;/li>
&lt;li>转换路径是显式的&lt;/li>
&lt;li>UI 行为由状态决定，而不是由事件临时判断&lt;/li>
&lt;/ul>
&lt;p>只是这些，在 Kotlin 里，编译器可以帮你约束；
在 JavaScript 里，只能靠你自己。&lt;/p></description></item><item><title>2025年终总结</title><link>https://opoojkk.com/posts/2025/12/29/2025/</link><pubDate>Mon, 29 Dec 2025 21:39:48 +0800</pubDate><guid>https://opoojkk.com/posts/2025/12/29/2025/</guid><description>&lt;p>又是一年年尾，又到了写年终总结的时候，写年终总结无非是敦促自己想想这一年做了什么，有什么进步，又有什么想做没做成的。&lt;/p>
&lt;p>细想这一年变化蛮大的，生活上、工作上都是。&lt;/p>
&lt;h2 class="heading" id="生活">
 生活&lt;span class="heading__anchor"> &lt;a href="#%e7%94%9f%e6%b4%bb">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>生活上，最大的变化就是从小一块长大的妹妹要结婚了。我是家里的独生子，妹妹是我大大家的，她上面还有一个亲哥。想着从小总能见到的妹妹要和另一个男生一起生活，过年不能再像之前一样打麻将，心里总归有些寂寞。&lt;/p>
&lt;p>一个朋友离开北京回老家了，别人做的选择只能尊重，即使我认为回家不是最好的选择。但是我以为好的，其他人并不一定这么认为。交集只会越来越少。&lt;/p>
&lt;p>年中离职前和入职现在的公司不久各养了一只猫，现在在养两只猫，一只叫岁岁，一只叫年年。要说什么寓意，岁岁平安，年年有余。开头的就是名字了，很俗，又很适合。刚好在前天两只一起做了绝育，变成了两只小公公猫。养猫的心情只有在养了之后才能体会，猫也是有自己的性格的，有的好动，有的好静，也有偶尔疯癫的，这才是一只完整的猫。&lt;/p>
&lt;p>今年还买了保险，想不起来是什么契机体会到生命有多脆弱，万一我出了什么意外，我父母该依靠什么继续生活。保险可以被依靠，每年花一笔钱，抵消掉意外的概率。如果发生了保险公司赔偿一笔钱，给我父母，如果没有那更好。买保险就不要想万一没发生怎么办，钱白花了，没有那回事。谁也不知道什么时候发生，保险换来的是面对的底气和保障。&lt;/p>
&lt;p>今年开始健身了，净身高173，体重才55kg，看上去瘦瘦巴巴的，随时要被风吹到。不是更早开始，是一直在纠结，是不是要找个人带着呢，又是不是做的不标准呢。到现在半年吧，上班占用的时间多，能去的次数少，也算是有些变化，还算满意。&lt;/p>
&lt;h2 class="heading" id="工作">
 工作&lt;span class="heading__anchor"> &lt;a href="#%e5%b7%a5%e4%bd%9c">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>至于工作上嘛，年中换了工作，结果不能满意。&lt;/p>
&lt;p>一直在找职业上方向，在想自己想做什么。体会过了不同的业务、不同的公司，加上平时的兴趣，还是希望做些不一样的事，单单实现业务没有乐趣。就好像是一个翻译，把产品逻辑翻译成代码。开发的职责也的确是实现产品的逻辑，又不止是这些。&lt;/p>
&lt;p>现在像是到了十字路口，接下来要怎么突破，选择往哪走，都还在摸索，无论什么样，都不应该满足于当前的状态。&lt;/p>
&lt;p>现在的大背景下，相信跨平台是趋势。看好跨平台，也看好响应式，即使现在不是，之后一定会是主流。&lt;/p>
&lt;p>AI 还在快速变化，适应 AI 的存在是一定需要的，也就在想如果 AI 能把现在在使用的场景做到极限，未来什么样的程序员是有价值的，我想做的还需要人完成吗，我以为是需要的。&lt;/p>
&lt;h2 class="heading" id="读书">
 读书&lt;span class="heading__anchor"> &lt;a href="#%e8%af%bb%e4%b9%a6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>今年和之前相比少得可怜，新的一年要重新拿起来。也带上几本给我印象深刻的书。&lt;/p>
&lt;ol>
&lt;li>《亲密关系》 - 哪些因素会影响我们选择伴侣&lt;/li>
&lt;li>《朱生豪书信全编》 - 朱生豪与宋清如的书信&lt;/li>
&lt;li>《精英的傲慢》 - 优绩主义的影响&lt;/li>
&lt;li>《老人与海》 - 精神的硬汉：圣地亚哥&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="新年目标">
 新年目标&lt;span class="heading__anchor"> &lt;a href="#%e6%96%b0%e5%b9%b4%e7%9b%ae%e6%a0%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ol>
&lt;li>花更多时间看书&lt;/li>
&lt;li>攒钱&lt;/li>
&lt;li>学会游泳&lt;/li>
&lt;li>掌握一门新的技能&lt;/li>
&lt;/ol>
&lt;p>大概就是这些，时间不早了，要洗洗睡了，祝大家平安快乐。&lt;/p></description></item><item><title>Compose Compiler 深入解析：从参数注入到重组执行的 IR 变换全流程</title><link>https://opoojkk.com/posts/2025/12/28/compose-compiler-ir-lowering-from-signature-to-recomposition/</link><pubDate>Sun, 28 Dec 2025 11:27:17 +0800</pubDate><guid>https://opoojkk.com/posts/2025/12/28/compose-compiler-ir-lowering-from-signature-to-recomposition/</guid><description>&lt;p>在 Compose 编译阶段，针对 &lt;code>@Composable&lt;/code> 函数，编译器主要做两件事：&lt;/p>
&lt;p>一是修改方法签名（参数注入），二是修改方法实现（函数体 Lowering）。
这两个步骤是分离实现、顺序执行的，分别对应两个核心 IR Transform。&lt;/p>
&lt;p>方法签名变换发生在 &lt;code>ComposerParamTransformer&lt;/code> 中，负责向 Composable 函数注入 &lt;code>$composer&lt;/code>、&lt;code>$changed&lt;/code>、&lt;code>$default&lt;/code> 等参数；
方法实现变换发生在 &lt;code>ComposableFunctionBodyTransformer&lt;/code> 中，负责生成 Group、处理默认参数、实现跳过与重组逻辑。&lt;/p>
&lt;p>这两个 Transform 的入口方法形式上都叫 &lt;code>visitFunction&lt;/code>，但它们承担的职责完全不同。&lt;/p>
&lt;p>源码位置如下：&lt;/p>
&lt;p>修改方法签名：
&lt;a href="https://cs.android.com/android-studio/kotlin/+/master:plugins/compose/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt">https://cs.android.com/android-studio/kotlin/+/master:plugins/compose/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt&lt;/a>&lt;/p>
&lt;p>修改方法实现：
&lt;a href="https://cs.android.com/android-studio/kotlin/+/master:plugins/compose/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt">https://cs.android.com/android-studio/kotlin/+/master:plugins/compose/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt&lt;/a>&lt;/p>
&lt;p>在进入函数体变换之前，有必要先理解签名变换阶段到底做了什么。&lt;/p>
&lt;p>ComposerParamTransformer 的核心目标很明确：
把一个“普通的 Kotlin 函数”，改造成一个“可以被 Compose Runtime 驱动的函数”。&lt;/p>
&lt;p>以一个最简单的 Composable 为例：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Composable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">A&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在签名层面，编译器会把它改写为（示意）：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">A&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">x&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">$&lt;/span>&lt;span class="n">composer&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Composer&lt;/span>&lt;span class="p">&amp;lt;*&amp;gt;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">$&lt;/span>&lt;span class="n">changed&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如果存在默认参数，还会注入 &lt;code>$default&lt;/code> 掩码参数。&lt;/p>
&lt;p>在 &lt;code>ComposerParamTransformer&lt;/code> 中，真正完成这件事的是 &lt;code>copyWithComposerParam&lt;/code> 这个扩展方法。它并不是“在原函数上就地修改”，而是深拷贝一个新的 &lt;code>IrSimpleFunction&lt;/code>，并将新旧函数建立映射关系，防止递归调用和符号错乱。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">IrSimpleFunction&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">copyWithComposerParam&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">IrSimpleFunction&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">assert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parameters&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">lastOrNull&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="nc">ComposeNames&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ComposerParameter&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">deepCopyWithSymbolsAndMetadata&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">also&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">fn&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">oldFn&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">this&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">transformedFunctionSet&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fn&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">transformedFunctions&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">oldFn&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">fn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">metadata&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">oldFn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">metadata&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">overriddenSymbols&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">oldFn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">overriddenSymbols&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">map&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">it&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">withComposerParamIfNeeded&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">symbol&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">..&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里一个容易被忽略但非常关键的点是 overriddenSymbols 的处理。
如果一个 Composable 函数重写了父类或接口方法（例如 &lt;code>setContent&lt;/code> 中的 &lt;code>Content()&lt;/code>），那么父类方法必须被同步注入 composer 参数，否则虚方法分发表会直接失效。&lt;/p>
&lt;p>接下来是参数注入本身。&lt;/p>
&lt;p>首先注入 &lt;code>$composer&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">composerParam&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">addValueParameter&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nc">ComposeNames&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ComposerParameter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">type&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">composerType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">makeNullable&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">origin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nc">IrDeclarationOrigin&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DEFINED&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">isAssignable&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>然后注入 &lt;code>$changed&lt;/code>，用于参数变化追踪与跳过优化：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="n">until&lt;/span> &lt;span class="n">changedParamCount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">realParams&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">thisParamCount&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">addValueParameter&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">$changed&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">$changed$i&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">irBuiltIns&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">intType&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里的 &lt;code>$changed&lt;/code> 并不是一个简单的 boolean，而是一个 bitmask。
每个参数占用固定数量的 bit，用来编码该参数在调用点已知的状态（是否稳定、是否变化、是否未知）。&lt;/p>
&lt;p>如果函数存在默认参数，还会额外注入 &lt;code>$default&lt;/code> 掩码参数，用来指示哪些参数在调用点被省略：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">requiresDefaultParameter&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="n">until&lt;/span> &lt;span class="n">defaultParamCount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">currentParams&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">addValueParameter&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">$default&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">$default$i&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">irBuiltIns&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">intType&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">IrDeclarationOrigin&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">MASK_FOR_DEFAULT_FUNCTION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>需要注意的是：Compose 并不依赖 Kotlin 原生的 &lt;code>$default&lt;/code> 桥接函数来处理默认参数，而是选择在 Composable 函数体内部显式展开默认值逻辑。这也是为什么默认参数的处理必须放在函数体变换阶段。&lt;/p>
&lt;p>在签名变换阶段，还有一段看起来有些“边角料”的逻辑：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">IrSimpleFunction&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">makeStubsForDefaultValueClassIfNeeded&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">IrSimpleFunction&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!is&lt;/span>&lt;span class="n">PublicComposableFunction&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">emptyList&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">stubs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">mutableListOf&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">IrSimpleFunction&lt;/span>&lt;span class="p">&amp;gt;()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">makeValueClassNonPrimitiveStub&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">let&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">stubs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">platform&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isJvm&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">makeValueClassInaccessibleConstructorDefaultStub&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">isPrimaryConstructorPrivate&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?.&lt;/span>&lt;span class="n">let&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">stubs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">makeValueClassInaccessibleConstructorDefaultStub&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">!&lt;/span>&lt;span class="n">constructorVisibilityIsAtLeastAsAccessibleAsType&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">let&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">stubs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">stubs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这段逻辑只在 public（或 published API）的 Composable 函数上触发，核心目的是处理 value class 作为默认参数时，在跨模块、构造器不可访问等场景下的 ABI 兼容问题。
这些 stub 的生成完全发生在参数变换阶段，它们的存在只是为了保证调用侧参数 patch 的一致性，本身并不会参与 Group 生成或重组逻辑。&lt;/p>
&lt;p>完成签名注入后，编译器才会进入第二阶段：函数体变换。&lt;/p>
&lt;p>ComposableFunctionBodyTransformer 的入口同样是 &lt;code>visitFunction&lt;/code>，但这里的重点已经不再是“函数长什么样”，而是“函数执行时如何与 Composer 交互”。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">visitFunction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">declaration&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">IrFunction&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">IrStatement&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">scope&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nc">Scope&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">FunctionScope&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">declaration&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">inScope&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">scope&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">visitFunctionInScope&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">declaration&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}.&lt;/span>&lt;span class="n">also&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metrics&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">recordFunction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">scope&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">metrics&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">declaration&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">functionMetrics&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">scope&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">metrics&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>FunctionScope&lt;/code> 是整个函数体 Lowering 的上下文核心，它在构造时会扫描函数参数，识别：&lt;/p>
&lt;ul>
&lt;li>是否存在 &lt;code>$composer&lt;/code>&lt;/li>
&lt;li>&lt;code>$changed&lt;/code> 参数集合&lt;/li>
&lt;li>&lt;code>$default&lt;/code> 参数集合&lt;/li>
&lt;li>实际业务参数数量&lt;/li>
&lt;li>Slot 数量&lt;/li>
&lt;/ul>
&lt;p>其中一个最重要的派生属性是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">isComposable&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">composerParameter&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里的“Composable”并不是指是否有 &lt;code>@Composable&lt;/code> 注解，而是指：
这个函数是否已经完成了 composer 参数注入，是否是一个真正可被 Runtime 驱动的 Composable 入口。&lt;/p>
&lt;p>接下来进入 &lt;code>visitFunctionInScope&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">visitFunctionInScope&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">declaration&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">IrFunction&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">IrStatement&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">scope&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">currentFunctionScope&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">scope&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isComposable&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">visitFunction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">declaration&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">declaration&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isDefaultParamStub&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">visitComposableFunctionStub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">declaration&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">declaration&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">origin&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">ADAPTER_FOR_CALLABLE_REFERENCE&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">visitComposableReferenceAdapter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">declaration&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">scope&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">..&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里可以看到，函数体变换阶段一开始就会过滤掉几类“不是 Composable 执行体”的函数：&lt;/p>
&lt;p>默认参数 stub、函数引用适配器，这些函数要么只是转发入口，要么只是桥接逻辑，本身不应该生成 Group，也不参与重组。&lt;/p>
&lt;p>接下来才是 Composable 函数的核心分流逻辑。&lt;/p>
&lt;p>编译器会基于函数特性，决定它属于哪一类：&lt;/p>
&lt;ul>
&lt;li>Composable Lambda&lt;/li>
&lt;li>可重启（Restartable）的普通 Composable&lt;/li>
&lt;li>不可重启（Non-restartable）的 Composable&lt;/li>
&lt;/ul>
&lt;p>是否可重启，取决于多个条件的综合判断，而不仅仅是“是否 inline”。
其中一个关键限制是：只有返回 Unit 的函数，才可能拥有独立的重组作用域（Restart Scope）。&lt;/p>
&lt;p>不可重启并不等于“不生成 Group”。
返回值的 Composable 函数依然需要 Group 来维护 Slot Table 状态，只是它们无法生成 &lt;code>startRestartGroup&lt;/code>，通常会生成 &lt;code>startReplaceableGroup&lt;/code>。&lt;/p>
&lt;p>在可重启函数中，编译器会生成典型的结构：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="err">$&lt;/span>&lt;span class="n">composer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startRestartGroup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 参数变化检测、默认参数展开、跳过逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">$&lt;/span>&lt;span class="n">composer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">endRestartGroup&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">updateScope&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">composer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">force&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">A&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">composer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="err">$&lt;/span>&lt;span class="n">changed&lt;/span> &lt;span class="n">or&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="err">$&lt;/span>&lt;span class="n">default&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里的 &lt;code>updateScope&lt;/code> Lambda，是 Runtime 在重组时重新执行该函数的入口。
&lt;code>$changed or 1&lt;/code> 的目的是强制设置最低位，避免在重组回调中再次被跳过。&lt;/p>
&lt;p>在函数体内部，&lt;code>$changed&lt;/code> 位掩码的传播起到了核心作用。
当一个 Composable 调用另一个 Composable 时，编译器会尽可能把当前已知的参数状态编码进被调用方的 &lt;code>$changed&lt;/code> 参数中，这一过程被称为 Comparison Propagation。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">B&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="m">123&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">$&lt;/span>&lt;span class="n">composer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="n">maskFromParent&lt;/span> &lt;span class="n">and&lt;/span> &lt;span class="n">dirty&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">or&lt;/span> &lt;span class="n">staticMask&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>通过这种方式，Runtime 可以在更深的调用层级上提前判断是否需要执行函数体，从而避免无意义的重组。&lt;/p>
&lt;p>至此可以看到，Compose Compiler 的整体设计非常清晰：&lt;/p>
&lt;p>第一阶段通过签名变换，把 Composable 函数改造成 Runtime 可调度的形态；
第二阶段通过函数体变换，构建 Group 结构、展开默认参数、实现跳过与重组逻辑。&lt;/p>
&lt;p>理解这一点之后，再回头看 &lt;code>remember&lt;/code>、&lt;code>mutableStateOf&lt;/code>、&lt;code>derivedStateOf&lt;/code> 等 API，就会发现它们并不是“魔法”，而只是对 Slot Table 与 Group 机制的不同使用方式。&lt;/p></description></item><item><title>为什么设置 clipChildren = false 可以突破父 View 边界？</title><link>https://opoojkk.com/posts/2025/12/21/why-clipchildren-false-breaks-parent-bounds/</link><pubDate>Sun, 21 Dec 2025 17:27:38 +0800</pubDate><guid>https://opoojkk.com/posts/2025/12/21/why-clipchildren-false-breaks-parent-bounds/</guid><description>&lt;h2 class="heading" id="设置clipchildren--false">
 设置clipChildren = false&lt;span class="heading__anchor"> &lt;a href="#%e8%ae%be%e7%bd%aeclipchildren--false">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>默认情况下，&lt;code>clipChildren&lt;/code> 是 &lt;strong>true&lt;/strong> 的。也就是说，在 &lt;code>ViewGroup&lt;/code> 绘制时，子 View 超出父 &lt;code>ViewGroup&lt;/code> 范围的部分会被裁剪掉。当然，这个行为是可以通过设置关闭的。&lt;/p>
&lt;p>如果将 &lt;code>clipChildren&lt;/code> 设置为 &lt;code>false&lt;/code>，允许子 View 绘制到父容器之外，Framework 内部到底做了什么？这篇就从源码角度来捋一捋这个过程。&lt;/p>
&lt;h2 class="heading" id="setclipchildren-做了哪些事">
 setClipChildren 做了哪些事？&lt;span class="heading__anchor"> &lt;a href="#setclipchildren-%e5%81%9a%e4%ba%86%e5%93%aa%e4%ba%9b%e4%ba%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>入口很清晰，就是 &lt;code>ViewGroup#setClipChildren&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * By default, children are clipped to their bounds before drawing. This
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * allows view groups to override this behavior for animations, etc.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param clipChildren true to clip children to their bounds,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * false otherwise
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @attr ref android.R.styleable#ViewGroup_clipChildren
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">setClipChildren&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">clipChildren&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">previousValue&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mGroupFlags&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FLAG_CLIP_CHILDREN&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FLAG_CLIP_CHILDREN&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">clipChildren&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">previousValue&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setBooleanFlag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FLAG_CLIP_CHILDREN&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">clipChildren&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mChildrenCount&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">child&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getChildAt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">child&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mRenderNode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">child&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mRenderNode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setClipToBounds&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">clipChildren&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">invalidate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>整体逻辑并不复杂，设置后一共做了四件事：&lt;/p>
&lt;ol>
&lt;li>判断之前的状态，避免重复设置；&lt;/li>
&lt;li>将 &lt;code>clipChildren&lt;/code> 状态保存到 &lt;code>ViewGroup&lt;/code> 的 flag 中；&lt;/li>
&lt;li>遍历所有子 View，同步更新其 &lt;code>RenderNode&lt;/code>；&lt;/li>
&lt;li>触发一次强制重绘。&lt;/li>
&lt;/ol>
&lt;p>其中，真正值得多看一眼的只有第 3 步。&lt;/p>
&lt;h2 class="heading" id="rendernodesetcliptobounds">
 RenderNode#setClipToBounds()&lt;span class="heading__anchor"> &lt;a href="#rendernodesetcliptobounds">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;code>View&lt;/code> 中的 &lt;code>mRenderNode&lt;/code> 是渲染系统的核心对象：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * RenderNode holding View properties, potentially holding a DisplayList of View content.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * When non-null and valid, this is expected to contain an up-to-date copy
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * of the View content. Its DisplayList content is cleared on temporary detach and reset on
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * cleanup.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@UnsupportedAppUsage&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RenderNode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mRenderNode&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当调用：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">child&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mRenderNode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setClipToBounds&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">clipChildren&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>本质上是在告诉渲染系统：
这个 View 对应的 RenderNode 是否需要将自身绘制内容裁剪到其 bounds 之内。&lt;/p>
&lt;p>继续往下看：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Set whether the Render node should clip itself to its bounds. This defaults to true,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * and is useful to the renderer in enable quick-rejection of chunks of the tree as well as
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * better partial invalidation support.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param clipToBounds true if the display list should clip to its bounds, false otherwise.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @return True if the value changed, false if the new value was the same as the previous value.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">setClipToBounds&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">clipToBounds&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nSetClipToBounds&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mNativeRenderNode&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">clipToBounds&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里已经很明确了：
clipChildren 最终并不是通过 Java 层 Canvas 裁剪实现的，而是直接下沉到了 RenderNode / HWUI 层。&lt;/p>
&lt;h2 class="heading" id="nsetcliptobounds-在-native-层">
 nSetClipToBounds 在 Native 层&lt;span class="heading__anchor"> &lt;a href="#nsetcliptobounds-%e5%9c%a8-native-%e5%b1%82">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>继续向下追，逻辑就进入了 AOSP 的 C++ 渲染管线中，这一点非常合理——毕竟最终执行绘制的是 HWUI。&lt;/p>
&lt;p>简单来说，&lt;code>nSetClipToBounds&lt;/code> 并不会立刻执行裁剪操作，而是：&lt;/p>
&lt;ul>
&lt;li>将 &lt;code>clipToBounds&lt;/code> 状态写入 &lt;code>RenderNode&lt;/code> 的属性对象中；&lt;/li>
&lt;li>这些属性会在后续的 RenderThread 同步阶段被读取；&lt;/li>
&lt;li>真正的裁剪行为发生在 DisplayList 录制、RenderNode 遍历以及渲染阶段。&lt;/li>
&lt;/ul>
&lt;p>也就是说：&lt;/p>
&lt;blockquote>
&lt;p>&lt;code>clipToBounds&lt;/code> 是一个&lt;strong>渲染属性声明&lt;/strong>，
而不是一次即时裁剪操作。
成员变量只是状态载体，真正的效果体现在后续渲染流程中。&lt;/p>
&lt;/blockquote>
&lt;p>如下图所示（保持原图）：&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/IUGBCaU.png">&lt;/figure>&lt;h2 class="heading" id="cliptobounds-如何真正影响绘制">
 clipToBounds 如何真正影响绘制？&lt;span class="heading__anchor"> &lt;a href="#cliptobounds-%e5%a6%82%e4%bd%95%e7%9c%9f%e6%ad%a3%e5%bd%b1%e5%93%8d%e7%bb%98%e5%88%b6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在 HWUI 的 RenderNode 树遍历过程中，&lt;code>clipToBounds&lt;/code> 会参与多项关键逻辑：&lt;/p>
&lt;ul>
&lt;li>Quick Reject（快速剔除）
用于快速判断某个 RenderNode 是否完全在可见区域之外；&lt;/li>
&lt;li>Dirty Region 计算
裁剪开启时，子节点的脏区域会被限制在父节点 bounds 内；&lt;/li>
&lt;li>DisplayList 裁剪指令生成
决定是否需要在 DisplayList 中插入裁剪约束。&lt;/li>
&lt;/ul>
&lt;p>当 &lt;code>clipChildren = false&lt;/code> 时：&lt;/p>
&lt;ul>
&lt;li>子 View 的 RenderNode 不再受父节点 bounds 约束；&lt;/li>
&lt;li>子 View 的绘制区域可以突破父容器；&lt;/li>
&lt;li>相应地，脏区域的合并范围也会变大。&lt;/li>
&lt;/ul>
&lt;p>换句话说：&lt;/p>
&lt;blockquote>
&lt;p>收集到的子 View 绘制区域，会在不受裁剪限制的情况下，
合并进已有的 dirty region，参与后续重绘。&lt;/p>
&lt;/blockquote>
&lt;h2 class="heading" id="小结">
 小结&lt;span class="heading__anchor"> &lt;a href="#%e5%b0%8f%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>从 AOSP 16 的实现来看，&lt;code>clipChildren&lt;/code> 的本质并不是一个简单的「是否裁剪 Canvas」开关，而是：&lt;/p>
&lt;ul>
&lt;li>一个 ViewGroup 层级的渲染策略声明&lt;/li>
&lt;li>通过 &lt;code>RenderNode&lt;/code> 下沉到 HWUI&lt;/li>
&lt;li>深度参与裁剪、脏区计算与渲染优化逻辑&lt;/li>
&lt;/ul>
&lt;p>这也解释了为什么它对动画、悬浮效果以及性能都有直接影响。&lt;/p></description></item><item><title>Android 字符串资源加载流程解析</title><link>https://opoojkk.com/posts/2025/12/07/android-string-resource-loading-pipeline/</link><pubDate>Sun, 07 Dec 2025 13:47:32 +0800</pubDate><guid>https://opoojkk.com/posts/2025/12/07/android-string-resource-loading-pipeline/</guid><description>&lt;h3 class="heading" id="android-字符串资源解析全流程解析">
 Android 字符串资源解析全流程解析&lt;span class="heading__anchor"> &lt;a href="#android-%e5%ad%97%e7%ac%a6%e4%b8%b2%e8%b5%84%e6%ba%90%e8%a7%a3%e6%9e%90%e5%85%a8%e6%b5%81%e7%a8%8b%e8%a7%a3%e6%9e%90">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>&lt;strong>说明：本文基于 Android API 36.0 和 Android Code Search（2025.12.7 最新 android-latest-release 分支）进行源码分析。&lt;/strong>&lt;/p>
&lt;p>在 Android 应用中，我们常写下这样一行代码：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">appName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">app_name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>看起来只是取一个字符串，但这行调用实际上穿过了从 Java 层到 Native 层的完整资源解析流程。下面以 &lt;strong>string&lt;/strong> 为例，看系统是如何一步步把资源 ID 转成最终的字符串。&lt;/p>
&lt;h2 class="heading" id="从-context-到-assetmanager">
 从 Context 到 AssetManager&lt;span class="heading__anchor"> &lt;a href="#%e4%bb%8e-context-%e5%88%b0-assetmanager">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>入口是 &lt;code>Context&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// frameworks/base/core/java/android/content/Context.java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getResources&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>ContextImpl&lt;/code> 内部持有 &lt;code>ResourcesImpl&lt;/code>，它最终把请求交给 &lt;code>AssetManager&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// frameworks/base/core/java/android/content/res/Resources.java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CharSequence&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@StringRes&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NotFoundException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">CharSequence&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mResourcesImpl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getAssets&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getResourceText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NotFoundException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;String resource ID #0x&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Integer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">toHexString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>getResourceText()&lt;/code> 会继续下沉到：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// frameworks/base/core/java/android/content/res/AssetManager.java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">getResourceValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">densityDpi&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">outValue&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">resolveRefs&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>进入 JNI 前执行：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// frameworks/base/core/java/android/content/res/AssetManager.java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">ensureValidLocked&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="jni-层统一的资源访问入口">
 JNI 层：统一的资源访问入口&lt;span class="heading__anchor"> &lt;a href="#jni-%e5%b1%82%e7%bb%9f%e4%b8%80%e7%9a%84%e8%b5%84%e6%ba%90%e8%ae%bf%e9%97%ae%e5%85%a5%e5%8f%a3">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Android 将所有&lt;strong>标量资源&lt;/strong>（string、color、integer、dimension 等）统一通过 &lt;code>NativeGetResourceValue()&lt;/code> 访问：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// frameworks/base/core/jni/android_util_AssetManager.cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="n">jint&lt;/span> &lt;span class="nf">NativeGetResourceValue&lt;/span>&lt;span class="p">(...){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">assetmanager&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LockAndStartAssetManager&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ptr&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">assetmanager&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetResource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">false&lt;/span> &lt;span class="cm">/*may_be_bag*/&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">density&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">has_value&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">ApkAssetsCookieToJavaCookie&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">kInvalidCookie&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">resolve_references&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">assetmanager&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ResolveReference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">has_value&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">ApkAssetsCookieToJavaCookie&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">kInvalidCookie&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">CopyValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">typed_value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="getresource查找资源识别类型">
 GetResource：查找资源、识别类型&lt;span class="heading__anchor"> &lt;a href="#getresource%e6%9f%a5%e6%89%be%e8%b5%84%e6%ba%90%e8%af%86%e5%88%ab%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// frameworks/base/libs/androidfw/AssetManager2.cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">auto&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FindEntry&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">density_override&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">has_value&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unexpected&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 如果 entry 是 map（style、array 等），则返回引用类型
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">auto&lt;/span> &lt;span class="n">map_entry&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">get_if&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">ResTable_map_entry&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">entry&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">may_be_bag&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unexpected&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">nullopt&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nf">SelectedValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TYPE_REFERENCE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">resid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">cookie&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">type_flags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">resid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 简单值：string、int、color
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Res_value&lt;/span> &lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Res_value&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">entry&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 处理动态资源 ID
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">dynamic_ref_table&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">lookupResourceValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">return&lt;/span> &lt;span class="nf">SelectedValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dataType&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">cookie&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">type_flags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">resid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="findentry拆解-id-并遍历所有设备配置">
 FindEntry：拆解 ID 并遍历所有设备配置&lt;span class="heading__anchor"> &lt;a href="#findentry%e6%8b%86%e8%a7%a3-id-%e5%b9%b6%e9%81%8d%e5%8e%86%e6%89%80%e6%9c%89%e8%ae%be%e5%a4%87%e9%85%8d%e7%bd%ae">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// frameworks/base/libs/androidfw/ResourceUtils.h
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">inline&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="nf">get_package_id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint32_t&lt;/span> &lt;span class="n">resid&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">static_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">resid&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="mi">24&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mh">0x000000ffu&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">inline&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="nf">get_type_id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint32_t&lt;/span> &lt;span class="n">resid&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">static_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">resid&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mh">0x000000ffu&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">inline&lt;/span> &lt;span class="kt">uint16_t&lt;/span> &lt;span class="nf">get_entry_id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint32_t&lt;/span> &lt;span class="n">resid&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">static_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint16_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resid&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mh">0x0000ffffu&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>遍历所有配置：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// frameworks/base/libs/androidfw/AssetManager2.cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">auto&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="nl">config&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">configurations_&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">ResTable_config&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">desired&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">density_override&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">density_override&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="n">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">density&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">density_override_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">config&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">density_override_config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">density&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">density_override&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">desired&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">density_override_config&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FindEntryInternal&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">package_group&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">type_idx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">entry_idx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">desired&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="findentryinternal匹配策略">
 FindEntryInternal：匹配策略&lt;span class="heading__anchor"> &lt;a href="#findentryinternal%e5%8c%b9%e9%85%8d%e7%ad%96%e7%95%a5">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// frameworks/base/libs/androidfw/AssetManager2.cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">entry_config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">match&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">requested_config&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">entry_config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isBetterThan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">best_config&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">requested_config&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">best&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">entry&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="res_value完整结构string--字符串池索引">
 Res_value：完整结构（string = 字符串池索引）&lt;span class="heading__anchor"> &lt;a href="#res_value%e5%ae%8c%e6%95%b4%e7%bb%93%e6%9e%84string--%e5%ad%97%e7%ac%a6%e4%b8%b2%e6%b1%a0%e7%b4%a2%e5%bc%95">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">Res_value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint16_t&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">res0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">enum&lt;/span> &lt;span class="err">: &lt;/span>&lt;span class="nc">uint8_t&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_NULL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x00&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_REFERENCE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x01&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_ATTRIBUTE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x02&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_STRING&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x03&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_FLOAT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x04&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_DIMENSION&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x05&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_FRACTION&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x06&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_DYNAMIC_REFERENCE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x07&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_DYNAMIC_ATTRIBUTE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x08&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_FIRST_INT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_INT_DEC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_INT_HEX&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x11&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_INT_BOOLEAN&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x12&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_FIRST_COLOR_INT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x1c&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_INT_COLOR_ARGB8&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x1c&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_INT_COLOR_RGB8&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x1d&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_INT_COLOR_ARGB4&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x1e&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_INT_COLOR_RGB4&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x1f&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_LAST_COLOR_INT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x1f&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TYPE_LAST_INT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x1f&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">dataType&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">enum&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_UNIT_SHIFT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_UNIT_MASK&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0xf&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_UNIT_PX&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_UNIT_DIP&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_UNIT_SP&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_UNIT_PT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_UNIT_IN&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_UNIT_MM&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_UNIT_FRACTION&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_UNIT_FRACTION_PARENT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_RADIX_SHIFT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_RADIX_MASK&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_RADIX_23p0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_RADIX_16p7&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_RADIX_8p15&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_RADIX_0p23&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_MANTISSA_SHIFT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">COMPLEX_MANTISSA_MASK&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0xffffff&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">enum&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DATA_NULL_UNDEFINED&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DATA_NULL_EMPTY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint32_t&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">copyFrom_dtoh&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Res_value&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">src&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="k">constexpr&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">kDeviceEndiannessSame&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span>&lt;span class="k">this&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">copyFrom_dtoh_slow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="异常处理">
 异常处理&lt;span class="heading__anchor"> &lt;a href="#%e5%bc%82%e5%b8%b8%e5%a4%84%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// frameworks/base/core/java/android/content/res/Resources.java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NotFoundException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;String resource ID #0x&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Integer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">toHexString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="加载流程图示意">
 加载流程图（示意）&lt;span class="heading__anchor"> &lt;a href="#%e5%8a%a0%e8%bd%bd%e6%b5%81%e7%a8%8b%e5%9b%be%e7%a4%ba%e6%84%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;figure>
 &lt;img src="https://i.imgur.com/sEzo9JN.png">&lt;/figure></description></item><item><title>C++ 学习笔记：指针与引用</title><link>https://opoojkk.com/posts/2025/11/30/cpp-pointer-and-reference-basics/</link><pubDate>Sun, 30 Nov 2025 13:26:05 +0800</pubDate><guid>https://opoojkk.com/posts/2025/11/30/cpp-pointer-and-reference-basics/</guid><description>&lt;h2 class="heading" id="指针是什么">
 指针是什么&lt;span class="heading__anchor"> &lt;a href="#%e6%8c%87%e9%92%88%e6%98%af%e4%bb%80%e4%b9%88">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在 Java 和 Kotlin 这些语言里，你不能直接操作内存地址，只需要关心生命周期，避免被生命周期更长的对象引用，进而导致内存不能释放。这是一种内存管理方案。而 C++ 则允许显式地操作内存——更具体一点，是允许直接处理指针或你手动分配的内存。Rust 又走了另一条路：归属模型。一块内存只能被一个变量拥有，其他变量想要用它必须“借”，目的是规避 C/C++ 中指针带来的野指针、悬空指针之类的问题。&lt;/p>
&lt;p>废话有点多，回到核心问题：指针到底是什么？&lt;/p>
&lt;p>维基百科给出的定义非常标准，但也非常“教科书”。简单来说，指针就是一个变量，它的值不是普通数据，而是&lt;strong>另一个对象的内存地址&lt;/strong>。也就是说，它保存的是“对象在哪里”，而不是“对象是什么”。&lt;/p>
&lt;p>举个最正常不过的例子：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>所有语言都能理解这句话：定义一个 &lt;code>int&lt;/code> 类型的变量 &lt;code>a&lt;/code>，它的值是 1。只不过在底层，这意味着分配了一块用来放 int 的内存空间，这块空间里存着数字 1。而 a 其实就是“访问这块内存时的入口”。&lt;/p>
&lt;p>如果我们能直接拿到这块内存的地址（假设是这块内存的起始位置），那还需要知道它是什么类型，才能知道要从这个地址往后读多少字节，才能正确还原数据的值。这就是为什么指针需要类型：&lt;code>int*&lt;/code> 不仅意味着“存的是地址”，还意味着“当解引用时，把那段内存按 int 解释”。&lt;/p>
&lt;p>所以，指针是什么？一句话：&lt;/p>
&lt;blockquote>
&lt;p>指针是一个保存某个对象内存地址的变量。它的类型决定你用它解引用时如何解析那段内存。&lt;/p>
&lt;/blockquote>
&lt;p>在 C++ 中，指针由 &lt;code>*&lt;/code> 声明：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>不要想太复杂，&lt;code>int*&lt;/code> 就是“指向 int 的指针类型”，和你写 &lt;code>int&lt;/code>、&lt;code>float&lt;/code> 一样，是类型的一部分。它可以在定义时就指向某个变量，也可以先定义再让它指向别的地方。&lt;/p>
&lt;p>既然说指针保存的是地址，那它就不能随便接受一个非地址的值，比如：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 不行
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>不是因为“指针不能赋值”，而是 1 并不是一个合法的地址，不能拿来解引用。&lt;/p>
&lt;p>接下来，既然地址存进去了，怎么获得地址里存放的数据呢？还是用 &lt;code>*&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>你注意到没有，这次 &lt;code>*&lt;/code> 在 &lt;code>=&lt;/code> 右边，表示“解引用”。总之如果 &lt;code>*&lt;/code> 没出现在类型声明里（&lt;code>int* p&lt;/code> 这种），它就是“从地址取值”。&lt;/p>
&lt;h2 class="heading" id="引用">
 引用&lt;span class="heading__anchor"> &lt;a href="#%e5%bc%95%e7%94%a8">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>前面说了，&lt;code>&amp;amp;a&lt;/code> 的含义是“取 a 的地址”。而如果你写：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这表示的是：开辟一块新的内存，把 &lt;code>a&lt;/code> 的值复制过去给 &lt;code>b&lt;/code>。两个变量各占一块地方。&lt;/p>
&lt;p>那有没有一种方式，不开辟第二块内存，只是给 a 再起一个名字，让另一个变量直接访问 a 的那块内存呢？有，这就是引用：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>b&lt;/code> 和 &lt;code>a&lt;/code> 是同一个对象的两个名字，修改 &lt;code>b&lt;/code> 就是在修改 &lt;code>a&lt;/code>。这也是为什么引用一旦绑定之后不能再改。&lt;/p>
&lt;p>注意，这里的 &lt;code>&amp;amp;&lt;/code> 和“取地址”的那个 &lt;code>&amp;amp;&lt;/code> 完全不是一个含义。现在它出现在 &lt;code>=&lt;/code> 左侧，也就是类型声明中，和 &lt;code>int* p&lt;/code> 里的 &lt;code>*&lt;/code> 类似，只是用来描述类型而已。&lt;/p>
&lt;h2 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;code>*&lt;/code> 和 &lt;code>&amp;amp;&lt;/code> 都很“善变”，放在不同位置含义完全不同。&lt;/p>
&lt;p>更准确地说，它们分为两种情况：
一种是出现在&lt;strong>类型声明&lt;/strong>中，比如 &lt;code>int*&lt;/code>、&lt;code>int&amp;amp;&lt;/code>；
另一种是出现在&lt;strong>表达式&lt;/strong>中，比如 &lt;code>*p&lt;/code>（解引用）、&lt;code>&amp;amp;a&lt;/code>（取地址）。&lt;/p>
&lt;p>这两类含义完全不同：&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>符号&lt;/th>
 &lt;th>出现在“类型声明”中&lt;/th>
 &lt;th>出现在“表达式”中&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>*&lt;/code>&lt;/td>
 &lt;td>声明“指针类型”&lt;/td>
 &lt;td>解引用（通过地址访问对象）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>&amp;amp;&lt;/code>&lt;/td>
 &lt;td>声明“引用类型”&lt;/td>
 &lt;td>取地址（得到指针）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div></description></item><item><title>从模拟信号到补码：计算机如何存储和计算数字</title><link>https://opoojkk.com/posts/2025/11/13/from-analog-to-twos-complement/</link><pubDate>Thu, 13 Nov 2025 21:07:40 +0800</pubDate><guid>https://opoojkk.com/posts/2025/11/13/from-analog-to-twos-complement/</guid><description>&lt;h2 class="heading" id="模电">
 模电&lt;span class="heading__anchor"> &lt;a href="#%e6%a8%a1%e7%94%b5">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>总常听到计算机的世界中只有0和1这两个，现实也是这样。但是自然界中的数据通常不是这样不是0就是1这般非黑即白的。像是声音、电流等等，都是连续的。&lt;/p>
&lt;p>为了能存储这些数据，就要转换成计算机的方式存储，那就是用二进制表示，怎么将连续的数值转换成二进制呢？先后经过模电（模拟电路）中的采样、量化、编码。&lt;/p>
&lt;p>如果不想理解其中的经过，那么作用像是下图这种&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/vUEplex.png" alt="ADC">&lt;/figure>&lt;p>想要更详细的理解，就需要付出一些时间了。&lt;/p>
&lt;h3 class="heading" id="采样">
 采样&lt;span class="heading__anchor"> &lt;a href="#%e9%87%87%e6%a0%b7">#&lt;/a>&lt;/span>
&lt;/h3>&lt;figure>
 &lt;img src="https://i.imgur.com/JSUQSqb.png" alt="ADC">&lt;/figure>&lt;p>上面是不同采样频率下得到的数据，得到的数据量是不同的，对应的，存储这些数据需要的空间也就不同。频率越高，损失的数据越少。&lt;/p>
&lt;h3 class="heading" id="量化">
 量化&lt;span class="heading__anchor"> &lt;a href="#%e9%87%8f%e5%8c%96">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>刚刚看的是横坐标，现在看纵坐标。每个点对应的纵坐标都对应着在这次采样中取到的数值，想要能记录下来，要把数值以一个相对值记录对吧。纵坐标上可能是许多不同的值，但是我们保存的时候会将这些数据按照不同的阶段对应到不同的值。&lt;/p>
&lt;p>拿电压举例：&lt;/p>
&lt;p>假设模拟信号是一个连续电压曲线：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">-1.0V → +1.0V
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>量化就是给这段范围分成若干&amp;quot;台阶&amp;quot;，比如 8 级：&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>电压范围&lt;/th>
 &lt;th>量化后值&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>-1.0V ~ -0.75V&lt;/td>
 &lt;td>0&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>-0.75V ~ -0.5V&lt;/td>
 &lt;td>1&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>-0.5V ~ -0.25V&lt;/td>
 &lt;td>2&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>-0.25V ~ 0.0V&lt;/td>
 &lt;td>3&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>0.0V ~ +0.25V&lt;/td>
 &lt;td>4&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>+0.25V ~ +0.5V&lt;/td>
 &lt;td>5&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>+0.5V ~ +0.75V&lt;/td>
 &lt;td>6&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>+0.75V ~ +1.0V&lt;/td>
 &lt;td>7&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>如果测得电压是 +0.63V，量化后结果是 6。&lt;/p>
&lt;h3 class="heading" id="编码">
 编码&lt;span class="heading__anchor"> &lt;a href="#%e7%bc%96%e7%a0%81">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>经过量化步骤得到的是十进制的数，开始说过计算机需要用二进制的数保存，这一步要做的就是这个——将十进制转换成二进制。&lt;/p>
&lt;h2 class="heading" id="数电">
 数电&lt;span class="heading__anchor"> &lt;a href="#%e6%95%b0%e7%94%b5">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>数电中通过不同的门电路和二进制完成数值的计算。补码的作用是为了简化减法计算，将减法转换成加法操作。&lt;/p>
&lt;p>为什么这样呢？&lt;/p>
&lt;ol>
&lt;li>加法操作比起减法需要更少的门电路，只是用一个完成加法即可。&lt;/li>
&lt;li>加法的进位比起减法的借位更容易，加法只可能向高位进一，不可能进更多，也就是每一位上计算最多需要用到低位上进来的一个数；减法不一样，可能会遇到连续借位的情况，这时候会更复杂。&lt;/li>
&lt;/ol>
&lt;p>硬件实现:&lt;/p>
&lt;p>直接减法器需要:&lt;/p>
&lt;ul>
&lt;li>借位逻辑电路(比进位复杂)&lt;/li>
&lt;li>大小比较电路&lt;/li>
&lt;li>符号处理电路&lt;/li>
&lt;li>可能的操作数交换电路&lt;/li>
&lt;li>结果取反电路&lt;/li>
&lt;/ul>
&lt;p>加法器 + 补码只需要:&lt;/p>
&lt;ul>
&lt;li>一个加法器&lt;/li>
&lt;li>一个取反器(NOT门)&lt;/li>
&lt;li>一个+1电路&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="补码">
 补码&lt;span class="heading__anchor"> &lt;a href="#%e8%a1%a5%e7%a0%81">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>铺垫完了，什么是补码呢？怎么执行？为什么这么设计？&lt;/p>
&lt;p>说白了补码是为了构建出和一个数比如是5，相加等于0的数，为了达成这个目的，有很多途径可以走。&lt;/p>
&lt;p>为了理解补码，我们先看看其他编码方式：&lt;/p>
&lt;h3 class="heading" id="原码符号原数字">
 原码（符号+原数字）&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e7%a0%81%e7%ac%a6%e5%8f%b7%e5%8e%9f%e6%95%b0%e5%ad%97">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c++" data-lang="c++">&lt;span class="line">&lt;span class="cl">&lt;span class="o">+&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mo">0000&lt;/span> &lt;span class="mo">0101&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">-&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1000&lt;/span> &lt;span class="mo">0101&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这样其实和减法没什么差别了，先要知道两个数的正负，再比较两个数的绝对值，决定结果的符号。&lt;/p>
&lt;h3 class="heading" id="反码">
 反码&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%8d%e7%a0%81">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c++" data-lang="c++">&lt;span class="line">&lt;span class="cl">&lt;span class="o">+&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mo">0000&lt;/span> &lt;span class="mo">0101&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">-&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1111&lt;/span> &lt;span class="mi">1010&lt;/span> &lt;span class="c1">// 只是取反，没有 +1
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这样看起来可以，但实际上有问题，会有两个0：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c++" data-lang="c++">&lt;span class="line">&lt;span class="cl">&lt;span class="o">+&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mo">0000&lt;/span> &lt;span class="mo">0000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">-&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1111&lt;/span> &lt;span class="mi">1111&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>两种形式都是0。&lt;/p>
&lt;p>还有另外一个问题，让我们举个例子计算5-3的结果。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c++" data-lang="c++">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">5&lt;/span> &lt;span class="err">的原码&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mo">0101&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">3&lt;/span> &lt;span class="err">的原码&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mo">0011&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">3&lt;/span> &lt;span class="err">的反码&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1100&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="err">符号位不变&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="err">其他位取反&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">5&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mo">0101&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">+&lt;/span> &lt;span class="mi">1100&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="mi">10001&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="err">产生进位&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>为什么需要把进位加到最低位呢？&lt;/p>
&lt;h3 class="heading" id="反码的数学本质">
 反码的数学本质&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%8d%e7%a0%81%e7%9a%84%e6%95%b0%e5%ad%a6%e6%9c%ac%e8%b4%a8">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>对于 n 位二进制数,负数 -x 的反码定义为:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">反码(-x) = (2^n - 1) - x
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这个定义直接决定了反码是模 2^n - 1 系统!&lt;/p>
&lt;h3 class="heading" id="为什么是-2n---1">
 为什么是 2^n - 1?&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e6%98%af-2n---1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>关键在于:反码是通过&amp;quot;按位取反&amp;quot;得到的&lt;/p>
&lt;h4 class="heading" id="按位取反的数学本质">
 按位取反的数学本质&lt;span class="heading__anchor"> &lt;a href="#%e6%8c%89%e4%bd%8d%e5%8f%96%e5%8f%8d%e7%9a%84%e6%95%b0%e5%ad%a6%e6%9c%ac%e8%b4%a8">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>对于 4 位二进制:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">3 的二进制: 0011
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">按位取反: 1100
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">这个1100代表什么数?
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>让我们计算:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">原数: 0011 = 0×8 + 0×4 + 1×2 + 1×1 = 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">取反后: 1100 = 1×8 + 1×4 + 0×2 + 0×1 = 12
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">它们的关系:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3 + 12 = 15 = 1111
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>15 就是 2^4 - 1&lt;/p>
&lt;h4 class="heading" id="通用规律">
 通用规律&lt;span class="heading__anchor"> &lt;a href="#%e9%80%9a%e7%94%a8%e8%a7%84%e5%be%8b">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>对于任意 n 位二进制数 x:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">x 的按位取反 = (2^n - 1) - x
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>证明:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">n位全1 = 111...1 (n个1) = 2^n - 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">例如:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4位: 1111 = 2^4 - 1 = 15
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">8位: 11111111 = 2^8 - 1 = 255
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">当我们对x按位取反时:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- x的某位是1 → 取反后是0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- x的某位是0 → 取反后是1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">这等价于:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(2^n - 1) - x
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>综上，反码实际上是基于模 (2^n - 1) 的运算系统。对于 4 位二进制:&lt;/p>
&lt;ul>
&lt;li>模是 2^4 - 1 = 15&lt;/li>
&lt;/ul>
&lt;p>这意味着在反码系统中,-3 实际上表示为:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">-3 ≡ 15 - 3 = 12 (mod 15)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">12 的二进制 = 1100
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="为什么需要循环进位">
 为什么需要循环进位&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81%e5%be%aa%e7%8e%af%e8%bf%9b%e4%bd%8d">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>让我们看完整的计算过程:&lt;/p>
&lt;p>5 - 3 的反码运算:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> 0101 (5)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+ 1100 (-3的反码)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 10001
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>现在关键来了:&lt;/p>
&lt;ul>
&lt;li>我们得到了 &lt;code>10001&lt;/code> (5位)&lt;/li>
&lt;li>但在 4 位反码系统中,模是 15 (1111)&lt;/li>
&lt;li>最高位的 &lt;code>1&lt;/code> 代表进位,意味着我们&amp;quot;超出了模&amp;quot;&lt;/li>
&lt;/ul>
&lt;p>数学上的解释:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">5 + 12 = 17 (十进制)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">17 mod 15 = 2 ← 这是我们要的答案
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">在二进制中:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">10001 = 16 + 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">16 mod 15 = 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">所以: 10001 mod 15 = 1 + 1 = 2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这个 &amp;ldquo;mod 15&amp;rdquo; 的操作在硬件上就是:把进位加回到最低位&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> 0001 (丢弃最高位)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+ 1 (循环进位)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0010 = 2 ✓
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这就是使用反码麻烦的原因。&lt;/p>
&lt;h3 class="heading" id="补码的设计">
 补码的设计&lt;span class="heading__anchor"> &lt;a href="#%e8%a1%a5%e7%a0%81%e7%9a%84%e8%ae%be%e8%ae%a1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>对于 n 位二进制数,负数 -x 的补码定义为:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">补码(-x) = 2^n - x
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这个定义直接决定了补码是模 2^n 系统!&lt;/p>
&lt;p>为什么不能是其他的数呢？刚刚的反码是2&lt;sup>n-1，证明过不行了。那么模能不能更大一些呢？比如如果模是 2&lt;/sup>n + 1：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">定义: 补码(-x) = (2^n + 1) - x
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">验证: x + (-x) = x + (2^n + 1 - x)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> = 2^n + 1 = 10001
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">丢弃进位后:0001,还是不是0! ✗
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>所以只有 2^n 这个模才能让补码系统完美工作。&lt;/p>
&lt;h2 class="heading" id="为什么内存中使用补码">
 为什么内存中使用补码&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e5%86%85%e5%ad%98%e4%b8%ad%e4%bd%bf%e7%94%a8%e8%a1%a5%e7%a0%81">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>说完了补码的设计原理，你可能会想：既然补码是为了简化硬件计算，那在内存里存储数据时，为什么也用补码呢？直接用原码存不是更直观吗？&lt;/p>
&lt;h3 class="heading" id="统一性原则">
 统一性原则&lt;span class="heading__anchor"> &lt;a href="#%e7%bb%9f%e4%b8%80%e6%80%a7%e5%8e%9f%e5%88%99">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>想象一下，如果内存用原码存储，ALU（算术逻辑单元）用补码计算，会发生什么？&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">内存里的 -5: 1000 0101 (原码)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 需要转换
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ALU 里的 -5: 1111 1011 (补码)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>每次从内存读取数据到CPU进行计算，都要做一次转换；计算完再存回内存，又要转换回去。这不是白白增加了复杂度吗？&lt;/p>
&lt;p>不如从头到尾都用同一套规则——这就是内存也使用补码的根本原因。&lt;/p>
&lt;h3 class="heading" id="存储也需要比较">
 存储也需要比较&lt;span class="heading__anchor"> &lt;a href="#%e5%ad%98%e5%82%a8%e4%b9%9f%e9%9c%80%e8%a6%81%e6%af%94%e8%be%83">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>内存里的数据不只是存着不动，还要做很多操作：&lt;/p>
&lt;ol>
&lt;li>比较大小：判断 a &amp;gt; b 吗？&lt;/li>
&lt;li>查找：在数组里找最大值&lt;/li>
&lt;li>排序：给一堆数排序&lt;/li>
&lt;/ol>
&lt;p>如果用原码，比较两个负数就很麻烦：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">原码中:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-3 = 1000 0011
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-5 = 1000 0101
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">直接按二进制比较: 1000 0101 &amp;gt; 1000 0011
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">得出结论: -5 &amp;gt; -3 ✗ 错了!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>因为原码的符号位和数值部分是分开的，比较负数时要单独处理。&lt;/p>
&lt;p>但在补码中：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">补码中:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-3 = 1111 1101
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-5 = 1111 1011
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">直接按二进制比较: 1111 1101 &amp;gt; 1111 1011
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">得出结论: -3 &amp;gt; -5 ✓ 对了!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>补码的巧妙之处在于：所有数（包括正数和负数）都可以直接按二进制大小比较，不需要特殊逻辑。&lt;/p>
&lt;h3 class="heading" id="节省硬件成本">
 节省硬件成本&lt;span class="heading__anchor"> &lt;a href="#%e8%8a%82%e7%9c%81%e7%a1%ac%e4%bb%b6%e6%88%90%e6%9c%ac">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>如果内存用原码，CPU用补码，就需要：&lt;/p>
&lt;ol>
&lt;li>数据总线上要有转换电路&lt;/li>
&lt;li>缓存控制器要处理转换逻辑&lt;/li>
&lt;li>DMA（直接内存访问）也要知道怎么转换&lt;/li>
&lt;/ol>
&lt;p>这些都是额外的硬件成本和功耗。&lt;/p>
&lt;p>而统一使用补码：&lt;/p>
&lt;ul>
&lt;li>数据从内存到CPU寄存器，直接传输，零开销&lt;/li>
&lt;li>存储和计算用同一套规则，硬件设计简单&lt;/li>
&lt;li>所有的加法器、比较器都可以共用&lt;/li>
&lt;/ul>
&lt;h3 class="heading" id="历史也是这么选择的">
 历史也是这么选择的&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%86%e5%8f%b2%e4%b9%9f%e6%98%af%e8%bf%99%e4%b9%88%e9%80%89%e6%8b%a9%e7%9a%84">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>早期计算机确实尝试过不同的方案：&lt;/p>
&lt;ul>
&lt;li>IBM 7090（1959）：用符号-数值表示（类似原码）&lt;/li>
&lt;li>CDC 6600（1964）：用补码&lt;/li>
&lt;li>现代所有处理器：都用补码&lt;/li>
&lt;/ul>
&lt;p>为什么最后都选择了补码？因为它在存储、传输、计算三个环节都是最优的选择。&lt;/p>
&lt;h2 class="heading" id="c中的自然溢出补码的意外之喜">
 C++中的自然溢出：补码的意外之喜&lt;span class="heading__anchor"> &lt;a href="#c%e4%b8%ad%e7%9a%84%e8%87%aa%e7%84%b6%e6%ba%a2%e5%87%ba%e8%a1%a5%e7%a0%81%e7%9a%84%e6%84%8f%e5%a4%96%e4%b9%8b%e5%96%9c">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>用了补码之后，还有一个意想不到的好处——溢出处理变得非常自然。&lt;/p>
&lt;h3 class="heading" id="什么是溢出">
 什么是溢出&lt;span class="heading__anchor"> &lt;a href="#%e4%bb%80%e4%b9%88%e6%98%af%e6%ba%a2%e5%87%ba">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>先看一个简单的例子，8位有符号整数（int8_t）的取值范围是 -128 到 127：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int8_t&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">127&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 最大值
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 发生了什么？
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在数学上，127 + 1 = 128，但 128 已经超出了 int8_t 的表示范围。这就是溢出。&lt;/p>
&lt;h3 class="heading" id="补码下的溢出行为">
 补码下的溢出行为&lt;span class="heading__anchor"> &lt;a href="#%e8%a1%a5%e7%a0%81%e4%b8%8b%e7%9a%84%e6%ba%a2%e5%87%ba%e8%a1%8c%e4%b8%ba">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>让我们看看在补码系统中实际发生了什么：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127 的补码: 0111 1111
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> +1: 0000 0001
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">结果: 1000 0000 = -128
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>127 + 1 = -128？这看起来很奇怪，但其实非常合理！&lt;/p>
&lt;p>这正是模 2^n 运算的自然结果：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127 + 1 = 128
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">128 mod 256 = 128
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">但在补码中，128 被解释为 -128
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="为什么这是自然的">
 为什么这是&amp;quot;自然&amp;quot;的？&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%bf%99%e6%98%af%e8%87%aa%e7%84%b6%e7%9a%84">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>关键在于：硬件不需要做任何额外的事情。&lt;/p>
&lt;p>看看如果用原码会怎样：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">原码系统中 127 + 1:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0111 1111 (127)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+ 0000 0001 (1)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1000 0000
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">这是 -0 还是 -128？硬件需要：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. 检测到溢出
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. 判断是上溢还是下溢
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. 设置溢出标志位
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. 可能触发异常
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>但在补码系统中：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">硬件只管：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. 做加法
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. 丢弃最高位的进位
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">就这么简单！结果自动&amp;#34;绕回&amp;#34;到正确的位置
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="实际例子循环计数器">
 实际例子：循环计数器&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%9e%e9%99%85%e4%be%8b%e5%ad%90%e5%be%aa%e7%8e%af%e8%ae%a1%e6%95%b0%e5%99%a8">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>这个特性在实际编程中非常有用，比如实现一个循环计数器：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">counter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 不断递增
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">300&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">counter&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 当 counter 到达 255 后，自动变成 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 不需要任何 if 判断！
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// counter 最终值是 44
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 因为 300 mod 256 = 44
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如果没有这种自然溢出，你需要这样写：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">counter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">300&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">counter&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">counter&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">255&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="c1">// 需要额外判断
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">counter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="有符号数的环形世界">
 有符号数的环形世界&lt;span class="heading__anchor"> &lt;a href="#%e6%9c%89%e7%ac%a6%e5%8f%b7%e6%95%b0%e7%9a%84%e7%8e%af%e5%bd%a2%e4%b8%96%e7%95%8c">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>补码让整数形成了一个环形结构：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">8位有符号整数的&amp;#34;环&amp;#34;:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -128
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> / \
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -127 127
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> / \
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -126 126
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | ... |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -1 → 0 → 1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>不断加 1：&lt;code>... → 126 → 127 → -128 → -127 → ...&lt;/code>&lt;/p>
&lt;p>不断减 1：&lt;code>... → -127 → -128 → 127 → 126 → ...&lt;/code>&lt;/p>
&lt;p>这个环形结构让很多算法的实现变得优雅：&lt;/p>
&lt;p>例子：判断两个时间戳的先后关系&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">uint32_t&lt;/span> &lt;span class="n">timestamp1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0xFFFFFFFE&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 接近最大值
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">uint32_t&lt;/span> &lt;span class="n">timestamp2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x00000001&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 溢出后的值
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 即使 timestamp2 的数值小于 timestamp1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 通过有符号减法可以正确判断
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">int32_t&lt;/span> &lt;span class="n">diff&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int32_t&lt;/span>&lt;span class="p">)(&lt;/span>&lt;span class="n">timestamp2&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">timestamp1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// diff = 3，说明 timestamp2 在后面
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="c-标准的规定">
 C++ 标准的规定&lt;span class="heading__anchor"> &lt;a href="#c-%e6%a0%87%e5%87%86%e7%9a%84%e8%a7%84%e5%ae%9a">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>需要注意的是，C++ 标准对溢出有不同的规定：&lt;/p>
&lt;p>无符号整数（unsigned）:&lt;/p>
&lt;ul>
&lt;li>溢出行为是明确定义的&lt;/li>
&lt;li>结果等于模 2^n&lt;/li>
&lt;li>完全依赖补码的自然特性&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">255&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 确保结果是 0
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>有符号整数（signed）:&lt;/p>
&lt;ul>
&lt;li>C++20 之前：溢出是未定义行为（UB）&lt;/li>
&lt;li>C++20 开始：有符号整数必须用补码，溢出变成实现定义行为&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int8_t&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">127&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// C++20: 结果是 -128 (但编译器可以优化)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// C++17: 未定义行为（危险！）
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>虽然实际上所有现代编译器都用补码，但在 C++17 及之前写代码时，最好避免依赖有符号数溢出的行为。&lt;/p>
&lt;h3 class="heading" id="硬件层面的简洁">
 硬件层面的简洁&lt;span class="heading__anchor"> &lt;a href="#%e7%a1%ac%e4%bb%b6%e5%b1%82%e9%9d%a2%e7%9a%84%e7%ae%80%e6%b4%81">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>最后总结一下，补码让溢出处理变得如此简单的原因：&lt;/p>
&lt;p>硬件需要做的：&lt;/p>
&lt;ol>
&lt;li>执行加法&lt;/li>
&lt;li>丢弃超出位数的进位&lt;/li>
&lt;/ol>
&lt;p>硬件不需要做的：&lt;/p>
&lt;ul>
&lt;li>不需要检查溢出&lt;/li>
&lt;li>不需要特殊处理边界值&lt;/li>
&lt;li>不需要区分有符号和无符号（加法器是同一个）&lt;/li>
&lt;li>不需要任何 if-else 逻辑&lt;/li>
&lt;/ul>
&lt;p>这就是为什么补码不仅简化了减法，还让整个整数运算系统变得如此优雅。&lt;/p>
&lt;p>总结一下：&lt;/p>
&lt;p>内存使用补码不是因为&amp;quot;不得不这样&amp;quot;，而是因为&amp;quot;这样最好&amp;quot;：&lt;/p>
&lt;ul>
&lt;li>和CPU的计算逻辑统一，无需转换&lt;/li>
&lt;li>比较操作可以直接用二进制大小判断&lt;/li>
&lt;li>硬件实现最简单，成本最低&lt;/li>
&lt;li>溢出行为自然，不需要额外处理逻辑&lt;/li>
&lt;/ul>
&lt;p>从内存到寄存器，从磁盘到网络传输，整个计算机系统都在用补码——这是一套经过时间检验的、最优雅的设计。&lt;/p></description></item><item><title>AAR 构建全解析:从解包到 APK 的完整生命周期</title><link>https://opoojkk.com/posts/2025/11/10/aar-build-lifecycle-complete-guide/</link><pubDate>Mon, 10 Nov 2025 21:25:23 +0800</pubDate><guid>https://opoojkk.com/posts/2025/11/10/aar-build-lifecycle-complete-guide/</guid><description>&lt;p>依赖里有一堆 AAR,最终 APK 里却不见它们的影子。&lt;/p>
&lt;p>不是它们消失了,而是它们一进构建系统就被解包,残片分别流向:&lt;/p>
&lt;ul>
&lt;li>dex&lt;/li>
&lt;li>manifest&lt;/li>
&lt;li>resources.arsc&lt;/li>
&lt;li>assets&lt;/li>
&lt;li>so&lt;/li>
&lt;/ul>
&lt;p>下面按真实的 AGP 源码线把整个事件复盘一遍。&lt;/p>
&lt;h2 class="heading" id="aar-生命周期的起点-extractaartransform">
 AAR 生命周期的起点: ExtractAarTransform&lt;span class="heading__anchor"> &lt;a href="#aar-%e7%94%9f%e5%91%bd%e5%91%a8%e6%9c%9f%e7%9a%84%e8%b5%b7%e7%82%b9-extractaartransform">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>AAR 被下载后,首先交给一个类处理:&lt;/p>
&lt;p>源码位置:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">system&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">gradle&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">core&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="o">/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">com&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">gradle&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">internal&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">dependency&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ExtractAarTransform&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">kt&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>源码链接: &lt;a href="https://android.googlesource.com/platform/tools/base/+/refs/heads/mirror-goog-studio-main/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/dependency/ExtractAarTransform.kt">ExtractAarTransform.kt&lt;/a>&lt;/p>
&lt;p>它的使命只有一个: 把一个 AAR 解压成若干个部分,每个部分给后续的构建步骤使用。&lt;/p>
&lt;blockquote>
&lt;p>注: 本文基于 AGP 7.x-8.x 版本分析。在 AGP 8.0+ 中,部分实现使用了新的 Artifact Transform API,但整体流程保持一致。&lt;/p>
&lt;/blockquote>
&lt;p>核心代码长这样:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ExtractAarTransform.kt (简化版)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">transform&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">TransformOutputs&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">File&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inputLibrary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LibraryIdentity&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ZipFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">input&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">use&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">zip&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">zip&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">entries&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">asSequence&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">forEach&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">entry&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">entry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isDirectory&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="nd">@forEach&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">entry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">outputFile&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">when&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// classes.jar: 库的主要字节码
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">path&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nc">SdkConstants&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">FN_CLASSES_JAR&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CLASSES_JAR&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// libs/*.jar: AAR 打包时包含的第三方依赖 jar
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startsWith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LIBS_PREFIX&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">endsWith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">SdkConstants&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DOT_JAR&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LIBS_DIR&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">substring&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">LIBS_PREFIX&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">length&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// AndroidManifest.xml: 库自己的 Manifest
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">path&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nc">SdkConstants&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">FN_ANDROID_MANIFEST_XML&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MANIFEST&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// res/: 资源文件 (layout、drawable、values 等)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startsWith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">SdkConstants&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">FD_RES&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="sc">&amp;#39;/&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RES_DIR&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">substring&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">4&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// assets/: 原始资源文件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startsWith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">SdkConstants&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">FD_ASSETS&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="sc">&amp;#39;/&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ASSETS_DIR&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">substring&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">7&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// jni/: native 库 (so 文件)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startsWith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">SdkConstants&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">FD_JNI&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="sc">&amp;#39;/&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">JNI_DIR&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">substring&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">4&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// R.txt: 资源符号表
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">path&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nc">SdkConstants&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">FN_RESOURCE_TEXT&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R_TXT&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// proguard.txt: 混淆规则
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">path&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nc">SdkConstants&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">FN_PROGUARD_TXT&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PROGUARD_RULES&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// lint.jar: Lint 检查规则
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">path&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">LINT_JAR_PATH&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outputs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LINT_JAR&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 将文件从 zip 中拷贝到输出目录
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">outputFile&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">let&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">file&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">zip&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getInputStream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">entry&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">use&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">input&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">outputStream&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">use&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">output&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">copyTo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>AAR 在这里完成它的历史使命——解包成构建系统真正需要的零件。&lt;/p>
&lt;p>剩下的流程,就是看这些碎片分别去哪。&lt;/p>
&lt;h2 class="heading" id="1-classesjar--libsjar--代码转换--dex">
 1. classes.jar + libs/*.jar → 代码转换 → dex&lt;span class="heading__anchor"> &lt;a href="#1-classesjar--libsjar--%e4%bb%a3%e7%a0%81%e8%bd%ac%e6%8d%a2--dex">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;code>classes.jar&lt;/code> 和 &lt;code>libs/&lt;/code> 目录下的 jar 文件拆出来之后,被交给代码处理链。&lt;/p>
&lt;p>源码位置:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">system&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">gradle&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">core&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="o">/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">com&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">gradle&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">internal&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">tasks&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">R8Task&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">kt&lt;/span> &lt;span class="c1"># Release 构建&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">com&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">gradle&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">internal&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">tasks&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">DexArchiveBuilderTask&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">kt&lt;/span> &lt;span class="c1"># Debug 构建&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>处理流程分两种情况:&lt;/p>
&lt;ul>
&lt;li>R8: 代码压缩 (ProGuard) + 优化 + 混淆 + dex 转换 (Release 模式)&lt;/li>
&lt;li>D8: 单纯的 class → dex 转换 (Debug 模式或禁用 R8 时)&lt;/li>
&lt;/ul>
&lt;p>关键入口大致是这样:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// R8 模式 (Release 构建) - 伪代码&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// AGP 7.x-8.0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">R8&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R8Command&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">builder&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addProgramFiles&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">classesJar&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 主程序 classes&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addLibraryFiles&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">androidJar&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Android SDK&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setMinApiLevel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">minSdk&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 最低 API&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addProguardConfigurationFiles&lt;/span>&lt;span class="p">(...)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 混淆规则 (包括 AAR 的 proguard.txt)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setOutput&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">outputDex&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OutputMode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">DexIndexed&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">build&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// D8 模式 (Debug 构建) - 伪代码&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">D8&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">D8Command&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">builder&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addProgramFiles&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">classesJar&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setMinApiLevel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">minSdk&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setOutput&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">outputDex&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OutputMode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">DexIndexed&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">build&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在这个过程中发生了什么:&lt;/p>
&lt;ol>
&lt;li>合并: 所有 AAR 的 classes.jar + libs/*.jar + 主工程的 class 文件一起处理&lt;/li>
&lt;li>依赖解析: 处理 AAR 之间的传递依赖关系,确保所有需要的类都被包含&lt;/li>
&lt;li>混淆规则收集: 所有 AAR 的 proguard.txt 被收集并应用到 R8/ProGuard&lt;/li>
&lt;li>转换: Java 字节码 (.class) → Dalvik 字节码 (.dex)&lt;/li>
&lt;li>分包: 如果方法数超过 65536,自动拆成多个 dex&lt;/li>
&lt;/ol>
&lt;p>最终输出:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">classes.dex
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">classes2.dex
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">classes3.dex
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>此时库的字节码已经和主工程代码一起,统一转换成了 dex 格式。&lt;/p>
&lt;h2 class="heading" id="2-androidmanifestxml--合并--最终-manifest">
 2. AndroidManifest.xml → 合并 → 最终 manifest&lt;span class="heading__anchor"> &lt;a href="#2-androidmanifestxml--%e5%90%88%e5%b9%b6--%e6%9c%80%e7%bb%88-manifest">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>库里的 manifest 文件会被送进合并器。&lt;/p>
&lt;p>源码路径:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">system&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">manifest&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">merger&lt;/span>&lt;span class="o">/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>源码链接: &lt;a href="https://android.googlesource.com/platform/tools/base/+/refs/heads/mirror-goog-studio-main/build-system/manifest-merger/">manifest-merger&lt;/a>&lt;/p>
&lt;p>核心入口是:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ManifestMerger2.java (简化逻辑)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">MergingReport&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">report&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ManifestMerger2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">newMerger&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mainManifest&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 主模块的 manifest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">logger&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">MergeType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">APPLICATION&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 应用类型 (还有 LIBRARY 类型)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addFlavorAndBuildTypeManifests&lt;/span>&lt;span class="p">(...)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// variant 的 manifest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addLibraryManifests&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libraryManifests&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// AAR 的 manifest 在这里加入&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">withFeatures&lt;/span>&lt;span class="p">(...)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 合并特性配置&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">merge&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 执行合并&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// 获取合并后的 manifest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">XmlDocument&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mergedDocument&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">report&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getMergedDocument&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>合并的核心逻辑:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 简化版的节点合并策略 (伪代码)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">XmlDocument&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">merge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">XmlDocument&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lowerPriorityDocument&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 优先级低的 (库)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">XmlDocument&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">higherPriorityDocument&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 优先级高的 (主模块)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 遍历低优先级文档的所有可合并元素&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">XmlElement&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">element&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lowerPriorityDocument&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getRootNode&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getMergeableElements&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 在高优先级文档中查找同类型、同 key 的节点&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Optional&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">XmlElement&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">higherPriorityNode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">higherPriorityDocument&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getNodeByTypeAndKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">element&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">higherPriorityNode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isPresent&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 存在冲突,按策略合并属性&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 默认: 高优先级覆盖低优先级&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mergeAttributes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">element&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">higherPriorityNode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 不冲突,直接添加到结果中&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">addNodeToMergedDocument&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">element&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mergedDocument&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>你看到的&amp;quot;主题被覆盖&amp;quot;&amp;ldquo;权限凭空出现&amp;quot;等现象都来源于这里。&lt;/p>
&lt;p>合并优先级 (从高到低):&lt;/p>
&lt;ol>
&lt;li>主模块 manifest&lt;/li>
&lt;li>buildType manifest (如 debug/release)&lt;/li>
&lt;li>flavor manifest (如 paid/free)&lt;/li>
&lt;li>依赖库 manifest (AAR)&lt;/li>
&lt;/ol>
&lt;p>依赖冲突处理: 当 AAR1 依赖 AAR2,且都声明了相同的组件时:&lt;/p>
&lt;ul>
&lt;li>按依赖解析顺序确定优先级&lt;/li>
&lt;li>直接依赖优先于传递依赖&lt;/li>
&lt;li>可使用 &lt;code>tools:replace&lt;/code>、&lt;code>tools:merge&lt;/code>、&lt;code>tools:remove&lt;/code> 等标记手动控制合并策略&lt;/li>
&lt;/ul>
&lt;p>合并完成后,AAR 自己的 manifest 文件即完成使命。&lt;/p>
&lt;h2 class="heading" id="3-res--编译--合并--资源表-resourcesarsc">
 3. res/ → 编译 → 合并 → 资源表 (resources.arsc)&lt;span class="heading__anchor"> &lt;a href="#3-res--%e7%bc%96%e8%af%91--%e5%90%88%e5%b9%b6--%e8%b5%84%e6%ba%90%e8%a1%a8-resourcesarsc">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>资源的处理是整个构建流程中最复杂的部分,分三个阶段。&lt;/p>
&lt;p>源码位置:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">sdk&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">common&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ide&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">common&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">resources&lt;/span>&lt;span class="o">/...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">aapt2&lt;/span>&lt;span class="o">/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="第一阶段-编译-compile">
 第一阶段: 编译 (compile)&lt;span class="heading__anchor"> &lt;a href="#%e7%ac%ac%e4%b8%80%e9%98%b6%e6%ae%b5-%e7%bc%96%e8%af%91-compile">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>每个资源文件单独编译成中间格式。&lt;/p>
&lt;p>源码入口:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">aapt2&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">compile&lt;/span>&lt;span class="o">/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Task 类: &lt;code>CompileLibraryResourcesTask&lt;/code> (AGP 7.x-8.0)&lt;/p>
&lt;p>处理流程:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// aapt2 compile (简化逻辑)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">CompileCommand&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">args&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="nl">filePath&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">inputFiles&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 读取资源文件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">ResourceFile&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">resource&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoadResourceFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">filePath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 编译成 flat 格式
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// flat 是 aapt2 的中间产物格式——每个资源文件编译后生成一个 .flat 文件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">CompiledFileOutputStream&lt;/span> &lt;span class="nf">out&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">outputPath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CompileResource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">out&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>例如:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">res/layout/activity_main.xml → activity_main.xml.flat
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">res/drawable/icon.png → icon.png.flat
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">res/values/strings.xml → values_strings.arsc.flat
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>每个 AAR 的资源文件、主模块的资源文件,都会各自生成一堆 &lt;code>.flat&lt;/code> 文件。&lt;/p>
&lt;h3 class="heading" id="第二阶段-链接-link">
 第二阶段: 链接 (link)&lt;span class="heading__anchor"> &lt;a href="#%e7%ac%ac%e4%ba%8c%e9%98%b6%e6%ae%b5-%e9%93%be%e6%8e%a5-link">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>所有 flat 文件被合并,生成最终的资源表和资源 ID。&lt;/p>
&lt;p>源码入口:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">aapt2&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">link&lt;/span>&lt;span class="o">/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Task 类: &lt;code>LinkApplicationAndroidResourcesTask&lt;/code> (AGP 7.x-8.0)&lt;/p>
&lt;p>处理流程:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// aapt2/link/Link.cpp (简化逻辑)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">LinkCommand&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">args&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. 加载所有 compiled resources (flat 文件)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">ResourceTable&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">tables&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="nl">input&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">inputFiles&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">table&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoadCompiledFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">input&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tables&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push_back&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">table&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. 加载 AAR 的 R.txt 符号表
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// R.txt 记录了库中资源的原始 ID,但这些 ID 会被重新分配
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="nl">rtxtPath&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">libraryRTxtFiles&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ResourceTable&lt;/span> &lt;span class="n">table&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ParseRTxt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rtxtPath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tables&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push_back&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">table&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. 合并资源表
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 所有 AAR 的资源 + 主模块的资源都在这里合并
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">ResourceTable&lt;/span> &lt;span class="n">finalTable&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">ResourceTable&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="nl">table&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">tables&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 检查资源冲突,执行合并策略
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">finalTable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Merge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">table&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 冲突处理: 主模块优先,AAR 资源被覆盖
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">ReportConflict&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 4. 重新分配资源 ID
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 每个资源分配一个唯一的 ID (0xPPTTEEEE 格式)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// PP (0x7f) = 应用资源包 ID (系统资源是 0x01)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// TT = 资源类型 ID (如 0x02=drawable, 0x0d=layout, 0x12=string)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// EEEE = 该类型下的资源条目 ID (Entry ID)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// AAR 原有的资源 ID 会被完全重新分配
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">IdAssigner&lt;/span> &lt;span class="nf">assigner&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">finalTable&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">assigner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AssignIds&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. 生成 resources.arsc (资源索引表)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">TableFlattener&lt;/span> &lt;span class="nf">flattener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">options&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">finalTable&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ofstream&lt;/span> &lt;span class="n">arscFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">outputDir&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s">&amp;#34;/resources.arsc&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ios&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">binary&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">flattener&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Flatten&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">arscFile&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 6. 生成 R.java (或针对每个包生成独立的 R.java)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 包含所有资源的 ID 常量
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">JavaClassGenerator&lt;/span> &lt;span class="nf">generator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">finalTable&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">generator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Generate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">packageName&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outputDir&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s">&amp;#34;/R.java&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 7. 打包非编译资源 (如 raw、图片原文件等)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">PackageResources&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">finalTable&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outputDir&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="第三阶段-编译-rjava--rjar">
 第三阶段: 编译 R.java → R.jar&lt;span class="heading__anchor"> &lt;a href="#%e7%ac%ac%e4%b8%89%e9%98%b6%e6%ae%b5-%e7%bc%96%e8%af%91-rjava--rjar">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>aapt2 link 生成的是 R.java 源文件,还需要编译:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 简化的编译流程&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// AGP 7.x-8.0: 独立的编译任务&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// AGP 8.0+: 整合到主编译流程中&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">javac&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">jar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cf&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">jar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">C&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>最终产物:&lt;/p>
&lt;ul>
&lt;li>resources.arsc: 资源索引表 (记录所有资源的 ID 和位置)&lt;/li>
&lt;li>R.jar: 包含 R.java 编译后的 class&lt;/li>
&lt;li>res/&amp;hellip; : 优化后的资源文件 (layout、drawable 等)&lt;/li>
&lt;/ul>
&lt;p>库的资源从此融入主工程,获得重新分配的统一资源 ID。&lt;/p>
&lt;h2 class="heading" id="4-assets--原样打包进入-apk">
 4. assets/ → 原样打包进入 APK&lt;span class="heading__anchor"> &lt;a href="#4-assets--%e5%8e%9f%e6%a0%b7%e6%89%93%e5%8c%85%e8%bf%9b%e5%85%a5-apk">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>这部分处理最简单,直接复制。&lt;/p>
&lt;p>源码位置:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">system&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">gradle&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">core&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="o">/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">com&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">gradle&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">internal&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">tasks&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">MergeSourceSetFolders&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">java&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>核心逻辑:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// MergeSourceSetFolders.java (简化逻辑)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@TaskAction&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">merge&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IOException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 收集所有 assets 目录 (主模块 + 所有 AAR)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">assetsSets&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getAssetsSets&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 直接复制到合并目录&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">assetsDir&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">assetsSets&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">assetsDir&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">exists&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 简单的文件树复制&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">FileUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">copyDirectory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">assetsDir&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">outputDir&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如果多个 AAR 或主模块有同名的 assets 文件,合并优先级:&lt;/p>
&lt;ol>
&lt;li>主模块 assets&lt;/li>
&lt;li>buildType assets&lt;/li>
&lt;li>flavor assets&lt;/li>
&lt;li>依赖库 assets (按依赖解析顺序,后解析的覆盖先解析的)&lt;/li>
&lt;/ol>
&lt;p>最终结果: 所有 assets 文件原样躺在 APK 的 &lt;code>assets/&lt;/code> 目录下。&lt;/p>
&lt;h2 class="heading" id="5-jni-so--abi-过滤--打包进入-apk">
 5. jni/ (so) → ABI 过滤 → 打包进入 APK&lt;span class="heading__anchor"> &lt;a href="#5-jni-so--abi-%e8%bf%87%e6%bb%a4--%e6%89%93%e5%8c%85%e8%bf%9b%e5%85%a5-apk">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>so 文件的处理在 native 库合并任务里完成。&lt;/p>
&lt;p>源码路径:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">system&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">gradle&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">core&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="o">/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">com&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">gradle&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">internal&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">tasks&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">MergeNativeLibsTask&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">kt&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>注: Task 类名在不同 AGP 版本可能略有差异,但逻辑一致。&lt;/p>
&lt;/blockquote>
&lt;p>核心逻辑:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// MergeNativeLibsTask.kt (简化逻辑)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nd">@TaskAction&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">taskAction&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 收集所有 so 文件 (主模块 + AAR)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">nativeLibs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">collectNativeLibs&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 获取 ABI 过滤配置
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">abiFilters&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">variantData&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ndkConfig&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">abiFilters&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">nativeLibs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">forEach&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">abi&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">soFiles&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 只处理配置中允许的 ABI
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">abiFilters&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isEmpty&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">abiFilters&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">contains&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">abi&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">soFiles&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">forEach&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">soFile&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 复制到对应的 ABI 目录
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">destFile&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">outputDir&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;lib/&lt;/span>&lt;span class="si">$abi&lt;/span>&lt;span class="s2">/&lt;/span>&lt;span class="si">${soFile.name}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">soFile&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">copyTo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">destFile&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">overwrite&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 不在过滤列表中的 ABI 直接丢弃
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>例如,如果你在 &lt;code>build.gradle&lt;/code> 中配置:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">android&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">defaultConfig&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ndk&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">abiFilters&lt;/span> &lt;span class="s1">&amp;#39;arm64-v8a&amp;#39;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="s1">&amp;#39;armeabi-v7a&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>那么 &lt;code>x86&lt;/code>、&lt;code>x86_64&lt;/code> 的 so 文件会被直接丢弃。&lt;/p>
&lt;p>最终输出结构:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── arm64-v8a/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ ├── libxxx.so
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ └── libyyy.so
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── armeabi-v7a/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── libxxx.so
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── libyyy.so
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="6-rtxt--参与资源链接">
 6. R.txt → 参与资源链接&lt;span class="heading__anchor"> &lt;a href="#6-rtxt--%e5%8f%82%e4%b8%8e%e8%b5%84%e6%ba%90%e9%93%be%e6%8e%a5">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>AAR 中的 &lt;code>R.txt&lt;/code> 记录了库内所有资源的名称、类型和在该库中的资源 ID。&lt;/p>
&lt;p>格式示例:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">int string app_name 0x7f120001
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">int layout activity_main 0x7f0d0025
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">int drawable icon 0x7f080003
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>资源 ID 格式说明 (0xPPTTEEEE):&lt;/p>
&lt;p>资源 ID 是 32 位整数,由三部分组成:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>PP (Package ID) : 应用资源包 ID&lt;/p>
&lt;ul>
&lt;li>&lt;code>0x7f&lt;/code> = 应用资源&lt;/li>
&lt;li>&lt;code>0x01&lt;/code> = 系统资源 (android.R.*)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>TT (Type ID) : 资源类型 ID&lt;/p>
&lt;ul>
&lt;li>&lt;code>0x02&lt;/code> = drawable&lt;/li>
&lt;li>&lt;code>0x0d&lt;/code> = layout&lt;/li>
&lt;li>&lt;code>0x12&lt;/code> = string&lt;/li>
&lt;li>等等&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>EEEE (Entry ID) : 该类型下的资源条目 ID (16 位,可表示 65536 个资源)&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>例如 &lt;code>0x7f0d0025&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>&lt;code>0x7f&lt;/code> = 应用资源包&lt;/li>
&lt;li>&lt;code>0x0d&lt;/code> = layout 类型&lt;/li>
&lt;li>&lt;code>0x0025&lt;/code> = 该 layout 类型下的第 37 号资源&lt;/li>
&lt;/ul>
&lt;p>重要: 这些 ID 只在库自己的构建中有效,当库被集成到主工程时,所有 ID 会被 aapt2 link 重新分配。&lt;/p>
&lt;p>这个文件在 aapt2 link 阶段被使用:&lt;/p>
&lt;p>处理位置:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">aapt2&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">link&lt;/span>&lt;span class="o">/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>参与生成:&lt;/p>
&lt;ol>
&lt;li>R.jar: 最终的 R class,包含所有资源 ID&lt;/li>
&lt;li>resources.arsc: 资源索引表中的符号信息&lt;/li>
&lt;/ol>
&lt;p>基本流程:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// aapt2 link 中读取 R.txt (简化逻辑)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">ResourceTable&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">librarySymbols&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="nl">rtxtPath&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">libraryRTxtFiles&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 解析 R.txt,提取资源名称和类型信息
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 原有的 ID 值会被忽略,稍后重新分配
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">ResourceTable&lt;/span> &lt;span class="n">table&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ParseRTxt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rtxtPath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">librarySymbols&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push_back&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">table&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 合并所有符号,分配统一的新 ID
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">MergeSymbols&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">librarySymbols&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">finalTable&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">AssignNewIds&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">finalTable&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="7-lintjar--lint-检查">
 7. lint.jar → Lint 检查&lt;span class="heading__anchor"> &lt;a href="#7-lintjar--lint-%e6%a3%80%e6%9f%a5">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>AAR 中的 &lt;code>lint.jar&lt;/code> 包含自定义的 Lint 检查规则。&lt;/p>
&lt;p>使用场景:&lt;/p>
&lt;p>库作者可以在 AAR 中打包自己的 Lint 规则,确保使用者正确使用该库的 API。&lt;/p>
&lt;p>处理位置:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">lint&lt;/span>&lt;span class="o">/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Task 类: &lt;code>AndroidLintTask&lt;/code>&lt;/p>
&lt;p>这些 lint.jar 会在代码检查阶段被加载:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Lint 检查时加载 AAR 的自定义规则 (简化逻辑)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lintJars&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">collectLintJarsFromAars&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">LintClient&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LintClient&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lintJar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lintJars&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addLintRules&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lintJar&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>实际案例:&lt;/p>
&lt;p>例如,Jetpack Compose 的 AAR 中就包含 lint.jar,用于检查:&lt;/p>
&lt;ul>
&lt;li>不正确的 Composable 函数命名 (必须以大写字母开头)&lt;/li>
&lt;li>状态管理最佳实践 (remember、rememberSaveable 的使用)&lt;/li>
&lt;li>性能相关的建议 (避免在 Composition 中进行耗时操作)&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="全链路流程图-源码版">
 全链路流程图 (源码版)&lt;span class="heading__anchor"> &lt;a href="#%e5%85%a8%e9%93%be%e8%b7%af%e6%b5%81%e7%a8%8b%e5%9b%be-%e6%ba%90%e7%a0%81%e7%89%88">#&lt;/a>&lt;/span>
&lt;/h2>&lt;figure>
 &lt;img src="https://i.imgur.com/NQDSLNG.png">&lt;/figure>&lt;h2 class="heading" id="为什么-aar-必须被解包">
 为什么 AAR 必须被解包?&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88-aar-%e5%bf%85%e9%a1%bb%e8%a2%ab%e8%a7%a3%e5%8c%85">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>你可能会问: 为什么不直接把 AAR 塞进 APK?&lt;/p>
&lt;p>因为 APK 的结构是固定的:&lt;/p>
&lt;ul>
&lt;li>dex: 所有代码必须合并成统一的字节码&lt;/li>
&lt;li>resources.arsc: 所有资源必须有统一的 ID 映射表&lt;/li>
&lt;li>AndroidManifest.xml: 只能有一个最终的 manifest&lt;/li>
&lt;/ul>
&lt;p>而 AAR 是&amp;quot;源码级别&amp;quot;的打包格式,它的存在是为了:&lt;/p>
&lt;ol>
&lt;li>方便分发 (不用发源码)&lt;/li>
&lt;li>包含完整信息 (代码 + 资源 + manifest + so + lint 规则)&lt;/li>
&lt;li>保持模块化 (每个库独立构建)&lt;/li>
&lt;/ol>
&lt;p>但构建 APK 时,这些信息必须&amp;quot;扁平化&amp;quot;成 APK 的标准结构。&lt;/p>
&lt;p>所以 AAR 必须被解包,没有其他选择。&lt;/p>
&lt;h2 class="heading" id="实际案例-recyclerview-aar-的解包">
 实际案例: RecyclerView AAR 的解包&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%9e%e9%99%85%e6%a1%88%e4%be%8b-recyclerview-aar-%e7%9a%84%e8%a7%a3%e5%8c%85">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>以 &lt;code>androidx.recyclerview:recyclerview:1.3.2&lt;/code> 为例:&lt;/p>
&lt;p>AAR 大小: 约 350 KB&lt;/p>
&lt;p>解包后内容:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">classes.jar → 280 KB (包含所有 RecyclerView 相关类)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">AndroidManifest.xml → 1 KB (声明最低 API 等基本信息)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">R.txt → 5 KB (约 100+ 个资源符号)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">res/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── values/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ ├── attrs.xml (自定义属性定义)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ └── public.xml (公开的资源 ID)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── layout/ (无,RecyclerView 无内置 layout)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">proguard.txt → 2 KB (保持规则,防止混淆关键类)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>构建后流向:&lt;/p>
&lt;ul>
&lt;li>&lt;code>classes.jar&lt;/code> → 经过 R8 混淆优化 → 合并到 &lt;code>classes.dex&lt;/code>&lt;/li>
&lt;li>&lt;code>AndroidManifest.xml&lt;/code> → 合并到主 manifest (贡献了 minSdkVersion 约束)&lt;/li>
&lt;li>&lt;code>res/values/attrs.xml&lt;/code> → 编译到 &lt;code>resources.arsc&lt;/code>&lt;/li>
&lt;li>&lt;code>R.txt&lt;/code> → 参与资源链接,生成新的资源 ID&lt;/li>
&lt;li>&lt;code>proguard.txt&lt;/code> → 确保 &lt;code>RecyclerView.LayoutManager&lt;/code> 等关键类不被混淆&lt;/li>
&lt;/ul>
&lt;p>最终 APK 中:&lt;/p>
&lt;ul>
&lt;li>代码大小: 约 180 KB (混淆优化后)&lt;/li>
&lt;li>资源增量: 约 3 KB (attrs 定义)&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="关键技术细节总结">
 关键技术细节总结&lt;span class="heading__anchor"> &lt;a href="#%e5%85%b3%e9%94%ae%e6%8a%80%e6%9c%af%e7%bb%86%e8%8a%82%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="资源-id-的重新分配">
 资源 ID 的重新分配&lt;span class="heading__anchor"> &lt;a href="#%e8%b5%84%e6%ba%90-id-%e7%9a%84%e9%87%8d%e6%96%b0%e5%88%86%e9%85%8d">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>很多人误以为 AAR 的资源 ID 会保留,实际上:&lt;/p>
&lt;ol>
&lt;li>AAR 中的 R.txt 记录的 ID (如 &lt;code>0x7f120001&lt;/code>) 只在该库构建时有效&lt;/li>
&lt;li>集成到主工程时,aapt2 link 会完全重新分配所有 ID&lt;/li>
&lt;li>最终每个资源得到一个全局唯一的新 ID&lt;/li>
&lt;li>这就是为什么你不能硬编码资源 ID 的根本原因&lt;/li>
&lt;/ol>
&lt;p>示例:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 错误做法 - 永远不要这样写!&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">errorId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0x7f120001&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 这个 ID 在不同构建中会变化&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">textView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">errorId&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// 正确做法&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">textView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">string&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">app_name&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 编译时会替换为实际 ID&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="混淆规则的收集">
 混淆规则的收集&lt;span class="heading__anchor"> &lt;a href="#%e6%b7%b7%e6%b7%86%e8%a7%84%e5%88%99%e7%9a%84%e6%94%b6%e9%9b%86">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>所有 AAR 的 proguard.txt 会被收集并应用:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">主模块 proguard-rules.pro
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+ AAR1 proguard.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+ AAR2 proguard.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+ ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 合并后的完整混淆规则
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 传递给 R8
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这确保了库的 API 不会被错误混淆。&lt;/p>
&lt;p>示例 (RecyclerView 的 proguard.txt):&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 保持 LayoutManager 及其子类不被混淆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">-&lt;/span>&lt;span class="n">keep&lt;/span> &lt;span class="n">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="k">extends&lt;/span> &lt;span class="n">androidx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">recyclerview&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">widget&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">RecyclerView&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">LayoutManager&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">public&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">init&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 保持 ViewHolder 不被混淆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">-&lt;/span>&lt;span class="n">keep&lt;/span> &lt;span class="n">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="k">extends&lt;/span> &lt;span class="n">androidx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">recyclerview&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">widget&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">RecyclerView&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">ViewHolder&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">public&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">init&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="manifest-合并的冲突解决">
 Manifest 合并的冲突解决&lt;span class="heading__anchor"> &lt;a href="#manifest-%e5%90%88%e5%b9%b6%e7%9a%84%e5%86%b2%e7%aa%81%e8%a7%a3%e5%86%b3">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>当多个 manifest 声明冲突时:&lt;/p>
&lt;ul>
&lt;li>优先级高的覆盖低的 (主模块 &amp;gt; buildType &amp;gt; flavor &amp;gt; 依赖)&lt;/li>
&lt;li>可以使用 &lt;code>tools:replace&lt;/code>、&lt;code>tools:merge&lt;/code>、&lt;code>tools:remove&lt;/code> 等标记控制策略&lt;/li>
&lt;li>构建时会生成详细的合并报告 (位于 &lt;code>build/outputs/logs/manifest-merger-*.txt&lt;/code>)&lt;/li>
&lt;/ul>
&lt;p>常见冲突示例:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- 主模块 manifest --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;application&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:theme=&lt;/span>&lt;span class="s">&amp;#34;@style/AppTheme&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">tools:replace=&lt;/span>&lt;span class="s">&amp;#34;android:theme&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span> &lt;span class="c">&amp;lt;!-- 强制覆盖库的 theme --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/application&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- AAR manifest --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;application&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:theme=&lt;/span>&lt;span class="s">&amp;#34;@style/LibraryTheme&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/application&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- 最终结果: 使用 AppTheme --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="传递依赖的处理">
 传递依赖的处理&lt;span class="heading__anchor"> &lt;a href="#%e4%bc%a0%e9%80%92%e4%be%9d%e8%b5%96%e7%9a%84%e5%a4%84%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>当依赖关系为 &lt;code>App → AAR1 → AAR2&lt;/code> 时:&lt;/p>
&lt;ol>
&lt;li>依赖解析: Gradle 首先解析整个依赖图,确定所有需要的 AAR&lt;/li>
&lt;li>版本冲突: 如果 AAR1 依赖 AAR2:1.0,主模块直接依赖 AAR2:2.0,默认选择 2.0 (最近优先)&lt;/li>
&lt;li>资源合并: 所有 AAR 的资源一起参与 aapt2 link,按优先级合并&lt;/li>
&lt;li>代码合并: 所有 AAR 的 classes.jar 一起传递给 R8/D8&lt;/li>
&lt;/ol>
&lt;p>依赖冲突解决策略:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span> &lt;span class="s1">&amp;#39;com.example:aar1:1.0&amp;#39;&lt;/span> &lt;span class="c1">// 内部依赖 aar2:1.0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">implementation&lt;/span> &lt;span class="s1">&amp;#39;com.example:aar2:2.0&amp;#39;&lt;/span> &lt;span class="c1">// 直接依赖,优先级更高
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 如果要强制使用特定版本
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">implementation&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s1">&amp;#39;com.example:aar1:1.0&amp;#39;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exclude&lt;/span> &lt;span class="nl">group:&lt;/span> &lt;span class="s1">&amp;#39;com.example&amp;#39;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">module:&lt;/span> &lt;span class="s1">&amp;#39;aar2&amp;#39;&lt;/span> &lt;span class="c1">// 排除传递依赖
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span> &lt;span class="s1">&amp;#39;com.example:aar2:1.5&amp;#39;&lt;/span> &lt;span class="c1">// 手动指定版本
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="总结-aar-的命运只有一个被解包">
 总结: AAR 的命运只有一个——被解包&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93-aar-%e7%9a%84%e5%91%bd%e8%bf%90%e5%8f%aa%e6%9c%89%e4%b8%80%e4%b8%aa%e8%a2%ab%e8%a7%a3%e5%8c%85">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>构建系统不需要&amp;quot;一个 AAR 文件&amp;rdquo;,它需要的只是:&lt;/p>
&lt;ul>
&lt;li>里面的 class → 转成 dex&lt;/li>
&lt;li>manifest → 合并到主 manifest&lt;/li>
&lt;li>res → 编译后合并到 resources.arsc,资源 ID 重新分配&lt;/li>
&lt;li>assets → 直接复制&lt;/li>
&lt;li>so → 按 ABI 过滤后复制&lt;/li>
&lt;li>R.txt → 参与资源链接,生成最终 R class&lt;/li>
&lt;li>proguard.txt → 参与代码混淆&lt;/li>
&lt;li>lint.jar → 提供自定义检查规则&lt;/li>
&lt;/ul>
&lt;p>所以 AAR 一进入系统就被解包,碎片分别走向不同的处理链,最后散落在:&lt;/p>
&lt;ul>
&lt;li>classes.dex (代码)&lt;/li>
&lt;li>resources.arsc (资源索引)&lt;/li>
&lt;li>AndroidManifest.xml (配置)&lt;/li>
&lt;li>assets/ (原始文件)&lt;/li>
&lt;li>lib/ (native 库)&lt;/li>
&lt;/ul>
&lt;p>AAR 自己不可能出现在 APK 内部。&lt;/p>
&lt;p>这,就是它的全部生命周期。&lt;/p>
&lt;h2 class="heading" id="附录-agp-版本差异说明">
 附录: AGP 版本差异说明&lt;span class="heading__anchor"> &lt;a href="#%e9%99%84%e5%bd%95-agp-%e7%89%88%e6%9c%ac%e5%b7%ae%e5%bc%82%e8%af%b4%e6%98%8e">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>本文基于 AGP 7.x-8.x 源码分析,不同版本的主要差异:&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>特性&lt;/th>
 &lt;th>AGP 7.x&lt;/th>
 &lt;th>AGP 8.0+&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Transform API&lt;/td>
 &lt;td>旧版 Transform API&lt;/td>
 &lt;td>新的 Artifact Transform API&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>R.java 编译&lt;/td>
 &lt;td>独立的 CompileRFilesTask&lt;/td>
 &lt;td>整合到主编译流程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>资源处理&lt;/td>
 &lt;td>基于 AAPT2&lt;/td>
 &lt;td>同样基于 AAPT2,优化了增量编译&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>依赖解析&lt;/td>
 &lt;td>Configuration API&lt;/td>
 &lt;td>同样,但增加了类型安全的 API&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Task 类名&lt;/td>
 &lt;td>MergeNativeLibs&lt;/td>
 &lt;td>MergeNativeLibsTask (名称统一化)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>核心流程保持一致,主要是 API 和性能优化上的改进。&lt;/p>
&lt;p>源码版本说明:&lt;/p>
&lt;p>本文基于 Android Gradle Plugin (AGP) 7.x-8.x 源码分析,路径均来自:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">https&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">googlesource&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">platform&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">tools&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="o">/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>不同 AGP 版本细节可能略有差异 (如 Task 类名、API 调用方式),但整体流程保持一致。AGP 8.0+ 引入了新的 Artifact Transform API,但 AAR 解包的本质流程未变。&lt;/p></description></item><item><title>命名的艺术：从驼峰到蛇形</title><link>https://opoojkk.com/posts/2025/11/05/the-art-of-naming/</link><pubDate>Wed, 05 Nov 2025 21:37:44 +0800</pubDate><guid>https://opoojkk.com/posts/2025/11/05/the-art-of-naming/</guid><description>&lt;p>不同编程语言往往有不同的命名约定。Java、Python、C++ 各自形成了独特的命名文化。许多重构类书籍在讲解前都会先强调命名，因为&lt;strong>命名绝不是一件简单的事&lt;/strong>。好的命名要能准确传达含义，让人一眼就能看出变量或函数的用途。
这些命名方式的共同目标，都是为了增强可读性——让名字更像语言，而不是符号。&lt;/p>
&lt;h2 class="heading" id="驼峰命名法camel-case">
 驼峰命名法（Camel Case）&lt;span class="heading__anchor"> &lt;a href="#%e9%a9%bc%e5%b3%b0%e5%91%bd%e5%90%8d%e6%b3%95camel-case">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>我最熟悉的语言是 Java，而 Java 最常见的命名方式就是&lt;strong>驼峰命名（camel case）&lt;/strong>。
在这种方式中，单词连接处用大写字母标识，其他字母保持小写。例如 &lt;code>firstName&lt;/code>。Java 中一般不会使用下划线 &lt;code>_&lt;/code> 来分隔单词。&lt;/p>
&lt;blockquote>
&lt;p>“驼峰式大小写（Camel-Case）一词来自 &lt;a href="https://zh.wikipedia.org/wiki/Perl">Perl&lt;/a> 语言中普遍使用的大小写混合格式，而 Larry Wall 等人所著的《Programming Perl》（&lt;a href="https://zh.wikipedia.org/wiki/O%27Reilly">O&amp;rsquo;Reilly&lt;/a> 出版）的封面是一匹骆驼，因此得名。”&lt;/p>
&lt;p>—— &lt;a href="https://zh.wikipedia.org/wiki/%E9%A7%9D%E5%B3%B0%E5%BC%8F%E5%A4%A7%E5%B0%8F%E5%AF%AB">维基百科 · 驼峰大小写&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>在 Java、Kotlin、Rust、Go、C++、Python 等语言中，成员变量和局部变量通常都会使用这种命名方式。&lt;/p>
&lt;h3 class="heading" id="帕斯卡命名法pascal-case">
 帕斯卡命名法（Pascal Case）&lt;span class="heading__anchor"> &lt;a href="#%e5%b8%95%e6%96%af%e5%8d%a1%e5%91%bd%e5%90%8d%e6%b3%95pascal-case">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>帕斯卡命名法可以看作驼峰命名法的一种变体。不同之处在于：&lt;strong>首字母也大写&lt;/strong>。例如 &lt;code>MyClass&lt;/code>。
在 Java 和 Kotlin 中，类名、接口名、文件名通常使用帕斯卡命名法。&lt;/p>
&lt;h2 class="heading" id="蛇形命名法snake-case">
 蛇形命名法（Snake Case）&lt;span class="heading__anchor"> &lt;a href="#%e8%9b%87%e5%bd%a2%e5%91%bd%e5%90%8d%e6%b3%95snake-case">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;strong>蛇形命名法（snake case）&lt;/strong>，也称“下划线命名法”。单词之间用下划线 &lt;code>_&lt;/code> 分隔。&lt;/p>
&lt;p>有两种变体：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>全大写&lt;/strong>：常用于常量，例如 &lt;code>MAX_VALUE&lt;/code>、&lt;code>MY_NAME&lt;/code>。这种写法在 Java、Kotlin、Rust、Python 中都很常见，C++ 的宏定义也沿用这种风格。&lt;/li>
&lt;li>&lt;strong>全小写&lt;/strong>：用于变量或函数名，尤其在 Python、Rust、C 语言中广泛使用，如 &lt;code>file_name&lt;/code>、&lt;code>get_value&lt;/code>。&lt;/li>
&lt;/ul>
&lt;p>一般来说，现代主流语言不会同时混用大小写与下划线分隔——那样既冗余又影响美观。&lt;/p>
&lt;h2 class="heading" id="烤串命名法kebab-case">
 烤串命名法（Kebab Case）&lt;span class="heading__anchor"> &lt;a href="#%e7%83%a4%e4%b8%b2%e5%91%bd%e5%90%8d%e6%b3%95kebab-case">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;strong>烤串命名法（kebab case）&lt;/strong> 使用小写字母并以短横线 &lt;code>-&lt;/code> 连接，例如：&lt;code>css-class-name&lt;/code>。
这种命名方式在前端开发中非常常见，尤其是 CSS 的类名或 HTML 属性名中。&lt;/p>
&lt;h2 class="heading" id="匈牙利命名法hungarian-notation">
 匈牙利命名法（Hungarian Notation）&lt;span class="heading__anchor"> &lt;a href="#%e5%8c%88%e7%89%99%e5%88%a9%e5%91%bd%e5%90%8d%e6%b3%95hungarian-notation">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;strong>匈牙利命名法（Hungarian notation）&lt;/strong> 是一种历史悠久、但如今较少使用的命名方式。
在 Java 时代，仍能看到有人使用，比如 &lt;code>mField&lt;/code>、&lt;code>sInstance&lt;/code>。
其规则是在名称前添加小写前缀，用以标识变量的类型或用途。例如：&lt;/p>
&lt;ul>
&lt;li>&lt;code>m&lt;/code> 表示成员变量（member）&lt;/li>
&lt;li>&lt;code>s&lt;/code> 表示静态变量（static）&lt;/li>
&lt;/ul>
&lt;p>根据用途不同，它分为两种：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>系统匈牙利命名法&lt;/strong>：源自早期的 BCPL 语言。当时语言层面不区分类型，于是开发者用前缀手动标注类型。&lt;/li>
&lt;li>&lt;strong>应用匈牙利命名法&lt;/strong>：后来演变为用前缀表示语义用途，而非类型，如 &lt;code>mField&lt;/code> 表示“成员字段”。&lt;/li>
&lt;/ul>
&lt;p>虽然这种方式在现代语言中已不再主流，但在老项目、甚至部分 Android 代码中依然能见到其痕迹。&lt;/p>
&lt;h2 class="heading" id="小结">
 小结&lt;span class="heading__anchor"> &lt;a href="#%e5%b0%8f%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>无论是驼峰、蛇形还是烤串，这些命名方式的核心目标始终一致——&lt;strong>让代码更易读、更易懂&lt;/strong>。
命名不仅是语法规则，更是沟通的语言。它连接着人和代码，也反映着语言设计的美学。&lt;/p>
&lt;p>&lt;strong>参考资料：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%E9%A7%9D%E5%B3%B0%E5%BC%8F%E5%A4%A7%E5%B0%8F%E5%AF%AB">驼峰大小写&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%E5%8C%88%E7%89%99%E5%88%A9%E5%91%BD%E5%90%8D%E6%B3%95">匈牙利命名法&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>函数与方法：一个容易被忽视的编程概念差异</title><link>https://opoojkk.com/posts/2025/10/30/function-vs-method-the-overlooked-difference/</link><pubDate>Thu, 30 Oct 2025 21:16:42 +0800</pubDate><guid>https://opoojkk.com/posts/2025/10/30/function-vs-method-the-overlooked-difference/</guid><description>&lt;p>从学习C语言接触计算机开始，我总以为函数和方法不过是两个不同的名称而已，只不过是习惯不同。直到阅读Rust语言的文档时，同时出现了&amp;quot;方法&amp;quot;和&amp;quot;函数&amp;quot;两个术语，我才意识到这其中的差异。&lt;/p>
&lt;p>事实上，它们不能说是两个完全不同的概念，但确实存在区别。&lt;/p>
&lt;h2 class="heading" id="什么是方法method">
 什么是方法(Method)&lt;span class="heading__anchor"> &lt;a href="#%e4%bb%80%e4%b9%88%e6%98%af%e6%96%b9%e6%b3%95method">#&lt;/a>&lt;/span>
&lt;/h2>&lt;blockquote>
&lt;p>在面向对象编程中，&lt;strong>方法&lt;/strong>（英语：Method；德语：Methode；法语：Méthode）指的是属于类别（所谓的类方法、静态方法或工厂方法）或者对象（所谓的实例方法）的子程序。如同过程化程序设计的程序，一个方法通常由一系列语句组成，并以此完成一个动作。它可以通过输入一组参数来指定所需的动作，且部分方法可能会有输出值（所谓的返回值）。&lt;strong>方法的目的是提供一个机制，以访问（读取和写入）对象或类别的私有数据&lt;/strong>。&lt;/p>
&lt;/blockquote>
&lt;p>参考：&lt;a href="https://en.wikipedia.org/wiki/Method_(computer_programming)">Wikipedia - Method (computer programming)&lt;/a>&lt;/p>
&lt;h2 class="heading" id="函数与方法的区别">
 函数与方法的区别&lt;span class="heading__anchor"> &lt;a href="#%e5%87%bd%e6%95%b0%e4%b8%8e%e6%96%b9%e6%b3%95%e7%9a%84%e5%8c%ba%e5%88%ab">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Wikipedia关于函数的条目中有这样一段描述：&lt;/p>
&lt;blockquote>
&lt;p>Some programming languages, such as COBOL and BASIC, make a distinction between functions that return a value (typically called &amp;ldquo;functions&amp;rdquo;) and those that do not (typically called &amp;ldquo;subprogram&amp;rdquo;, &amp;ldquo;subroutine&amp;rdquo;, or &amp;ldquo;procedure&amp;rdquo;); some, such as C, C++, and Rust, only use the term &amp;ldquo;function&amp;rdquo; irrespective of whether they return a value or not; others, such as ALGOL 60 and PL/I, only use the word procedure. &lt;strong>Some object-oriented languages, such as Java and C#, refer to functions inside classes as &amp;ldquo;methods&amp;rdquo;.&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;p>参考：&lt;a href="https://en.wikipedia.org/wiki/Function_(computer_programming)">Wikipedia - Function (computer programming)&lt;/a>&lt;/p>
&lt;p>如果你去搜索相关讨论，会发现基本能形成的共识是：&lt;strong>定义在类内部的称为Method（方法），定义在类外部的称为Function（函数）&lt;/strong>。&lt;/p>
&lt;h2 class="heading" id="各语言中的实践">
 各语言中的实践&lt;span class="heading__anchor"> &lt;a href="#%e5%90%84%e8%af%ad%e8%a8%80%e4%b8%ad%e7%9a%84%e5%ae%9e%e8%b7%b5">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>理解了这个理论后，我们再来看各种语言的实现就清晰多了。&lt;/p>
&lt;h3 class="heading" id="java">
 Java&lt;span class="heading__anchor"> &lt;a href="#java">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Java中所有的&amp;quot;函数&amp;quot;都必须定义在类中，无论是成员方法还是静态方法，因此统一称为&lt;strong>方法&lt;/strong>。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Calculator&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">offset&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 实例方法&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 静态方法&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">multiply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// 使用&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Calculator&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">calc&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Calculator&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">calc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">2&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 调用实例方法&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Calculator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">multiply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">3&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">4&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 调用静态方法&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="kotlin">
 Kotlin&lt;span class="heading__anchor"> &lt;a href="#kotlin">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Kotlin既可以像Java一样在类内部定义，这些被称为&lt;strong>方法&lt;/strong>；也可以定义在类的外部，因此扩展函数被称为&amp;quot;扩展&lt;strong>函数&lt;/strong>&amp;quot;，而不是&amp;quot;扩展方法&amp;quot;。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 顶层函数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">fun&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">Int&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Calculator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">private&lt;/span> &lt;span class="k">var&lt;/span> &lt;span class="py">offset&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 成员方法
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">Int&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="n">offset&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 扩展函数（注意是&amp;#34;函数&amp;#34;不是&amp;#34;方法&amp;#34;）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">fun&lt;/span> &lt;span class="nf">Calculator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">subtract&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">Int&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="p">-&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="c">
 C++&lt;span class="heading__anchor"> &lt;a href="#c">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>C++同时支持函数和方法，但在术语上都统称为&amp;quot;function&amp;quot;。&lt;/p>
&lt;p>&lt;strong>函数(Function)&lt;/strong>：定义在类外部的独立函数&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 全局函数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 命名空间中的函数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">namespace&lt;/span> &lt;span class="n">Math&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="nf">multiply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>方法(Method)&lt;/strong>：定义在类内部的成员函数&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Calculator&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 实例方法（成员函数）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 静态方法
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="nf">multiply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">offset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 使用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Calculator&lt;/span> &lt;span class="n">calc&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">calc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 调用实例方法
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">Calculator&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">multiply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 调用静态方法
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="go">
 Go&lt;span class="heading__anchor"> &lt;a href="#go">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Go语言明确区分函数和方法的概念。&lt;/p>
&lt;p>&lt;strong>函数(Function)&lt;/strong>：独立定义的函数&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 包级别函数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">func&lt;/span> &lt;span class="nf">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">result&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nf">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>方法(Method)&lt;/strong>：绑定到特定类型的函数（通过接收者receiver定义）&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">type&lt;/span> &lt;span class="nx">Calculator&lt;/span> &lt;span class="kd">struct&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">offset&lt;/span> &lt;span class="kt">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 值接收者方法
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">c&lt;/span> &lt;span class="nx">Calculator&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">b&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">c&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">offset&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 指针接收者方法
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">c&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Calculator&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">SetOffset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">offset&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">c&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">offset&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">offset&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 使用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">calc&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">Calculator&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">offset&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">calc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// 调用方法
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">calc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">SetOffset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// 调用方法
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="rust">
 Rust&lt;span class="heading__anchor"> &lt;a href="#rust">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Rust也明确区分函数和方法，这也是最初让我意识到这个差异的语言。&lt;/p>
&lt;p>&lt;strong>函数(Function)&lt;/strong>：独立定义的自由函数&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 自由函数(free function)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>: &lt;span class="kt">i32&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>: &lt;span class="kt">i32&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kt">i32&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// 模块中的函数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">mod&lt;/span> &lt;span class="nn">math&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">pub&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">multiply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>: &lt;span class="kt">i32&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>: &lt;span class="kt">i32&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kt">i32&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>方法(Method)&lt;/strong>：在&lt;code>impl&lt;/code>块中为类型定义的函数&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Calculator&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">offset&lt;/span>: &lt;span class="kt">i32&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Calculator&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 关联函数(Associated Function) - 类似静态方法，没有self参数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">offset&lt;/span>: &lt;span class="kt">i32&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">Self&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Calculator&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">offset&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 实例方法 - 带有self参数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>: &lt;span class="kt">i32&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>: &lt;span class="kt">i32&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kt">i32&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">offset&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 可变引用方法
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">set_offset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">offset&lt;/span>: &lt;span class="kt">i32&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">offset&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// 使用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">calc&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Calculator&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 调用关联函数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">calc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 调用实例方法
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">calc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">set_offset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 调用可变方法
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>语言&lt;/th>
 &lt;th>函数(Function)&lt;/th>
 &lt;th>方法(Method)&lt;/th>
 &lt;th>特点&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>Java&lt;/strong>&lt;/td>
 &lt;td>不存在&lt;/td>
 &lt;td>所有都是方法&lt;/td>
 &lt;td>必须定义在类中&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>Kotlin&lt;/strong>&lt;/td>
 &lt;td>顶层函数、扩展函数&lt;/td>
 &lt;td>类成员方法&lt;/td>
 &lt;td>同时支持两者&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>C++&lt;/strong>&lt;/td>
 &lt;td>全局函数、命名空间函数&lt;/td>
 &lt;td>类成员函数&lt;/td>
 &lt;td>术语上都称为function&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>Go&lt;/strong>&lt;/td>
 &lt;td>包级别函数&lt;/td>
 &lt;td>带接收者的函数&lt;/td>
 &lt;td>明确区分，方法必须有receiver&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>Rust&lt;/strong>&lt;/td>
 &lt;td>自由函数、模块函数&lt;/td>
 &lt;td>impl块中的函数&lt;/td>
 &lt;td>明确区分，有self的是方法，无self的是关联函数&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>&lt;strong>核心区别其实很简单：方法(Method)与类型/对象绑定，函数(Function)独立存在。&lt;/strong>&lt;/p></description></item><item><title>Android依赖管理解析：从ext到 Version Catalog的演进</title><link>https://opoojkk.com/posts/2025/10/23/android-dependency-management-evolution/</link><pubDate>Thu, 23 Oct 2025 20:55:28 +0800</pubDate><guid>https://opoojkk.com/posts/2025/10/23/android-dependency-management-evolution/</guid><description>&lt;p>在 Android 多模块项目中，依赖管理（Dependency Management）往往被低估。
最开始你可能只是写几个 &lt;code>implementation &amp;quot;xxx:xxx:1.0.0&amp;quot;&lt;/code>，但随着模块数量增长、团队协作增加、版本升级频繁，依赖的统一管理会直接影响项目的可维护性与构建效率。&lt;/p>
&lt;p>目前常见的几种依赖管理方式包括：&lt;/p>
&lt;ol>
&lt;li>最普通的直接写依赖；&lt;/li>
&lt;li>&lt;code>ext&lt;/code> 属性集中定义；&lt;/li>
&lt;li>使用 &lt;code>buildSrc&lt;/code> 代码模块；&lt;/li>
&lt;li>使用 Gradle 7+ 的版本目录（Version Catalog）。&lt;/li>
&lt;/ol>
&lt;p>下面我们从示例、优势与不足的角度，依次剖析这些方案。&lt;/p>
&lt;h2 class="heading" id="直接写依赖">
 直接写依赖&lt;span class="heading__anchor"> &lt;a href="#%e7%9b%b4%e6%8e%a5%e5%86%99%e4%be%9d%e8%b5%96">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>最朴素的方式是把依赖写在每个模块的 &lt;code>build.gradle&lt;/code> 中。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.core:core-ktx:1.10.1&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;com.squareup.retrofit2:retrofit:2.9.0&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">testImplementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;junit:junit:4.13.2&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>优势&lt;/p>
&lt;ul>
&lt;li>简单直接，几乎不需要学习成本。&lt;/li>
&lt;li>适合小型或一次性项目。&lt;/li>
&lt;li>所有依赖可一眼看清楚。&lt;/li>
&lt;/ul>
&lt;p>不足&lt;/p>
&lt;ul>
&lt;li>多模块时容易重复定义。&lt;/li>
&lt;li>升级版本需要全局修改，容易漏。&lt;/li>
&lt;li>难以审查依赖一致性。&lt;/li>
&lt;li>无法实现全局统一管理。&lt;/li>
&lt;/ul>
&lt;p>这种方式适合 Demo 或临时项目，一旦模块数超过三个，就会让人后悔没早点集中管理。&lt;/p>
&lt;h2 class="heading" id="使用-ext-属性集中管理">
 使用 &lt;code>ext&lt;/code> 属性集中管理&lt;span class="heading__anchor"> &lt;a href="#%e4%bd%bf%e7%94%a8-ext-%e5%b1%9e%e6%80%a7%e9%9b%86%e4%b8%ad%e7%ae%a1%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在根 &lt;code>build.gradle&lt;/code> 中用 &lt;code>ext&lt;/code> 定义变量：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ext&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">versions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">coreKtx:&lt;/span> &lt;span class="s2">&amp;#34;1.10.1&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">retrofit:&lt;/span> &lt;span class="s2">&amp;#34;2.9.0&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">okhttpLogging:&lt;/span> &lt;span class="s2">&amp;#34;5.0.0-alpha.2&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">junit:&lt;/span> &lt;span class="s2">&amp;#34;4.13.2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">libs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">coreKtx:&lt;/span> &lt;span class="s2">&amp;#34;androidx.core:core-ktx:${versions.coreKtx}&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">retrofit:&lt;/span> &lt;span class="s2">&amp;#34;com.squareup.retrofit2:retrofit:${versions.retrofit}&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">okhttpLogging:&lt;/span> &lt;span class="s2">&amp;#34;com.squareup.okhttp3:logging-interceptor:${versions.okhttpLogging}&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">junit:&lt;/span> &lt;span class="s2">&amp;#34;junit:junit:${versions.junit}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在子模块中这样使用：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span> &lt;span class="n">libs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">coreKtx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span> &lt;span class="n">libs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">retrofit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>优势&lt;/p>
&lt;ul>
&lt;li>集中管理版本号与依赖坐标。&lt;/li>
&lt;li>易于迁移、上手成本低。&lt;/li>
&lt;li>小中型项目的过渡方案。&lt;/li>
&lt;/ul>
&lt;p>不足&lt;/p>
&lt;ul>
&lt;li>仍然缺乏类型安全与自动提示。&lt;/li>
&lt;li>配置多了容易混乱。&lt;/li>
&lt;li>修改根配置会触发子项目重新评估，影响构建速度。&lt;/li>
&lt;li>难以管理插件或 bundle。&lt;/li>
&lt;/ul>
&lt;p>&lt;code>ext&lt;/code> 是一种“权宜之计”：比纯手写好一点，但距离真正的现代依赖管理还差几步。&lt;/p>
&lt;h2 class="heading" id="使用-buildsrc-模块">
 使用 &lt;code>buildSrc&lt;/code> 模块&lt;span class="heading__anchor"> &lt;a href="#%e4%bd%bf%e7%94%a8-buildsrc-%e6%a8%a1%e5%9d%97">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在根目录下创建 &lt;code>buildSrc&lt;/code> 目录，Gradle 会自动编译并加载其中代码。
可以在这里定义依赖与版本常量。&lt;/p>
&lt;p>&lt;code>buildSrc/src/main/kotlin/Dependencies.kt&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">object&lt;/span> &lt;span class="nc">Versions&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">coreKtx&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.10.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">retrofit&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;2.9.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">okhttpLogging&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;5.0.0-alpha.2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">junit&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;4.13.2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">object&lt;/span> &lt;span class="nc">Libs&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">coreKtx&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;androidx.core:core-ktx:&lt;/span>&lt;span class="si">${Versions.coreKtx}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">retrofit&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.squareup.retrofit2:retrofit:&lt;/span>&lt;span class="si">${Versions.retrofit}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">okhttpLogging&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.squareup.okhttp3:logging-interceptor:&lt;/span>&lt;span class="si">${Versions.okhttpLogging}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">junit&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;junit:junit:&lt;/span>&lt;span class="si">${Versions.junit}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在模块中使用：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">Libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">retrofit&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">Libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">okhttpLogging&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>优势&lt;/p>
&lt;ul>
&lt;li>类型安全、IDE 自动补全。&lt;/li>
&lt;li>可以定义插件、扩展函数等构建逻辑。&lt;/li>
&lt;li>依赖集中、可复用性强。&lt;/li>
&lt;li>适合中大型项目，特别是多团队协作。&lt;/li>
&lt;/ul>
&lt;p>不足&lt;/p>
&lt;ul>
&lt;li>修改 &lt;code>buildSrc&lt;/code> 会强制重新编译整个项目。&lt;/li>
&lt;li>对构建时间有一定影响。&lt;/li>
&lt;li>学习与维护成本更高。&lt;/li>
&lt;li>对于仅做依赖管理来说略显“重量级”。&lt;/li>
&lt;/ul>
&lt;p>&lt;code>buildSrc&lt;/code> 是“工程师式”的方案，强大、灵活，但稍有过度设计风险。&lt;/p>
&lt;h2 class="heading" id="使用版本目录version-catalog">
 使用版本目录（Version Catalog）&lt;span class="heading__anchor"> &lt;a href="#%e4%bd%bf%e7%94%a8%e7%89%88%e6%9c%ac%e7%9b%ae%e5%bd%95version-catalog">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Gradle 7.0 起推荐使用的现代方案。
在 &lt;code>gradle/libs.versions.toml&lt;/code> 中集中声明所有依赖。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">versions&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">coreKtx&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.10.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">retrofit&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;2.9.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">okhttpLogging&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;5.0.0-alpha.2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">junit&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;4.13.2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">libraries&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">androidx-core-ktx&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">group&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;androidx.core&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;core-ktx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">version&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ref&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;coreKtx&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">retrofit&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">group&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.squareup.retrofit2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;retrofit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">version&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ref&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;retrofit&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">okhttp-logging-interceptor&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">group&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.squareup.okhttp3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;logging-interceptor&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">version&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ref&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;okhttpLogging&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">junit&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">group&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;junit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;junit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">version&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ref&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;junit&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">bundles&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">network&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;retrofit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;okhttp-logging-interceptor&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在模块中直接使用：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">core&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ktx&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">retrofit&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">okhttp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">logging&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">interceptor&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>优势&lt;/p>
&lt;ul>
&lt;li>版本与依赖完全集中。&lt;/li>
&lt;li>IDE 自动提示强、语法简洁。&lt;/li>
&lt;li>不会触发全项目重编译。&lt;/li>
&lt;li>支持 bundle、plugin、version alias 等现代特性。&lt;/li>
&lt;li>官方推荐、未来趋势明确。&lt;/li>
&lt;/ul>
&lt;p>不足&lt;/p>
&lt;ul>
&lt;li>迁移成本高，需要调整依赖引用方式。&lt;/li>
&lt;li>无法直接在 &lt;code>buildSrc&lt;/code> 中使用。&lt;/li>
&lt;li>对团队约定要求较高（别名统一、文件管理规范）。&lt;/li>
&lt;li>对于简单项目可能显得过度。&lt;/li>
&lt;/ul>
&lt;p>Version Catalog 已成为新标准，兼顾类型安全、性能与可维护性。&lt;/p>
&lt;h2 class="heading" id="对比与建议">
 对比与建议&lt;span class="heading__anchor"> &lt;a href="#%e5%af%b9%e6%af%94%e4%b8%8e%e5%bb%ba%e8%ae%ae">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>方式&lt;/th>
 &lt;th>学习成本&lt;/th>
 &lt;th>版本集中度&lt;/th>
 &lt;th>类型安全&lt;/th>
 &lt;th>构建性能&lt;/th>
 &lt;th>适用场景&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>直接写依赖&lt;/td>
 &lt;td>最低&lt;/td>
 &lt;td>★&lt;/td>
 &lt;td>无&lt;/td>
 &lt;td>优&lt;/td>
 &lt;td>Demo、小项目&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>ext&lt;/code>&lt;/td>
 &lt;td>低&lt;/td>
 &lt;td>★★&lt;/td>
 &lt;td>弱&lt;/td>
 &lt;td>良&lt;/td>
 &lt;td>中小项目&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>buildSrc&lt;/code>&lt;/td>
 &lt;td>中&lt;/td>
 &lt;td>★★★&lt;/td>
 &lt;td>强&lt;/td>
 &lt;td>一般&lt;/td>
 &lt;td>中大型项目&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Version Catalog&lt;/td>
 &lt;td>中等&lt;/td>
 &lt;td>★★★★★&lt;/td>
 &lt;td>强&lt;/td>
 &lt;td>最优&lt;/td>
 &lt;td>大型、多团队项目&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>建议：&lt;/p>
&lt;ul>
&lt;li>新项目建议直接上 Version Catalog。&lt;/li>
&lt;li>已有项目可从 &lt;code>ext&lt;/code> 或 &lt;code>buildSrc&lt;/code> 逐步迁移。&lt;/li>
&lt;li>对于大型项目，可混合使用：Version Catalog 管理版本号，&lt;code>buildSrc&lt;/code> 管理构建逻辑。&lt;/li>
&lt;li>无论哪种方式，都应建立“依赖声明规范”，禁止模块私自引入未注册依赖。&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="结语">
 结语&lt;span class="heading__anchor"> &lt;a href="#%e7%bb%93%e8%af%ad">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>依赖管理是 Android 工程化的地基之一。
当项目规模上升、团队扩大、构建频率增加，越早建立清晰、集中、可扩展的依赖管理体系，就越能避免未来的混乱。
选择哪种方式取决于项目规模与团队成熟度，但如果你打算长期维护一个 Android 应用，那就别犹豫——从 Version Catalog 开始。&lt;/p></description></item><item><title>一次 Android APK 体积优化实录</title><link>https://opoojkk.com/posts/2025/10/22/android-apk-size-optimization-practice/</link><pubDate>Wed, 22 Oct 2025 21:48:06 +0800</pubDate><guid>https://opoojkk.com/posts/2025/10/22/android-apk-size-optimization-practice/</guid><description>&lt;h2 class="heading" id="现状及问题">
 现状及问题&lt;span class="heading__anchor"> &lt;a href="#%e7%8e%b0%e7%8a%b6%e5%8f%8a%e9%97%ae%e9%a2%98">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>最近工作上有些变动，新接手的项目中发现 apk 的 Debug 包有 170 多 MB，Release 包也要 140 MB。
仔细看项目内容，其实并没有那么多东西。想要减小体积，首先得搞清楚从哪里下手，于是决定先分析一下应用的体积构成。&lt;/p>
&lt;h2 class="heading" id="分析">
 分析&lt;span class="heading__anchor"> &lt;a href="#%e5%88%86%e6%9e%90">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>将打包后的 apk 拖到 &lt;strong>Android Studio&lt;/strong> 里，就能看到各部分的体积占比。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/Rlt6d5o.png">&lt;/figure>&lt;h3 class="heading" id="abi">
 ABI&lt;span class="heading__anchor"> &lt;a href="#abi">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>&lt;code>lib&lt;/code> 的体积挺惊人，占了 92.9MB。
这个目录是放 native 库的，来源通常有两种：&lt;/p>
&lt;ol>
&lt;li>自己写的 native 代码（为了性能、安全或通用性）；&lt;/li>
&lt;li>第三方库自带的 native 文件。&lt;/li>
&lt;/ol>
&lt;p>第三方库为了支持更多设备，会同时包含多个 ABI。
先从这里下手，初步猜测是没过滤 ABI。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/2el2yGR.png">&lt;/figure>&lt;p>果然，ABI 支持得很全，连不用的都打进去了。&lt;/p>
&lt;p>ABI（Application Binary Interface）指的是“应用二进制接口”，
也就是应用（或其原生库）与系统或其他模块在二进制层面所遵循的规则，比如指令集、函数调用约定、数据对齐、库格式、命名修饰（mangling）等。
简单来说，ABI 就是 &lt;strong>CPU 支持的指令集&lt;/strong>。&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>ABI 名称&lt;/th>
 &lt;th>架构 / 指令集&lt;/th>
 &lt;th>状态说明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>armeabi&lt;/code>&lt;/td>
 &lt;td>ARMv5（32 位）&lt;/td>
 &lt;td>早期架构，NDK r17 起已废弃。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>armeabi-v7a&lt;/code>&lt;/td>
 &lt;td>ARMv7（32 位） + Thumb-2 + NEON&lt;/td>
 &lt;td>早期主流架构，现已逐步淘汰。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>arm64-v8a&lt;/code>&lt;/td>
 &lt;td>ARMv8（64 位）&lt;/td>
 &lt;td>当前主流，Google Play 要求支持。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>x86&lt;/code>&lt;/td>
 &lt;td>Intel/AMD 32 位&lt;/td>
 &lt;td>市场占比极低，仅部分设备或模拟器使用。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>x86_64&lt;/code>&lt;/td>
 &lt;td>Intel/AMD 64 位&lt;/td>
 &lt;td>同上，主要用于模拟器。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>mips&lt;/code>,&lt;code>mips64&lt;/code>&lt;/td>
 &lt;td>MIPS 架构&lt;/td>
 &lt;td>曾支持，现已移除。&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;h3 class="heading" id="当前架构情况2025">
 当前架构情况（2025）&lt;span class="heading__anchor"> &lt;a href="#%e5%bd%93%e5%89%8d%e6%9e%b6%e6%9e%84%e6%83%85%e5%86%b52025">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>&lt;strong>ARMv8-A（64 位）&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>ABI：&lt;code>arm64-v8a&lt;/code>&lt;/li>
&lt;li>市场占比超过 90%&lt;/li>
&lt;li>特点：支持 AArch64/AArch32 双指令集，兼容性好&lt;/li>
&lt;li>代表芯片：骁龙 8 Gen4、天玑 9400、Exynos 2400&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ARMv7-A（32 位）&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>ABI：&lt;code>armeabi-v7a&lt;/code>&lt;/li>
&lt;li>适用：老旧中低端设备（2015–2020）&lt;/li>
&lt;li>现状：新机基本不再使用，仅残留于部分 IoT 设备&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>x86 / x86_64&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>主要用于安卓模拟器和少量平板&lt;/li>
&lt;li>市场占比低于 3%&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>当前手机基本都是 64 位 ARM 架构，许多 armv8 设备都已经卡得不行，更不用说更早的 armv7。
过滤 ABI 很简单，在模块级 &lt;code>gradle&lt;/code> 中加上：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gradle" data-lang="gradle">&lt;span class="line">&lt;span class="cl">&lt;span class="n">abiFilters&lt;/span> &lt;span class="s1">&amp;#39;arm64-v8a&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>省掉了 &lt;strong>66.86MB&lt;/strong>。&lt;/p>
&lt;h3 class="heading" id="资源">
 资源&lt;span class="heading__anchor"> &lt;a href="#%e8%b5%84%e6%ba%90">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>第二大块体积来自资源。
Android 资源包含：图片、布局、字体、字符串、颜色、属性。
打包时通常会开启 &lt;code>isShrinkResources&lt;/code> 去掉未使用资源，但正在使用的图片仍然会占据相当大空间。&lt;/p>
&lt;p>图片体积往往以 MB 计，尤其是大尺寸 PNG。
常见格式有：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>PNG&lt;/strong>：无损压缩，清晰但体积大；&lt;/li>
&lt;li>&lt;strong>JPG&lt;/strong>：有损压缩，体积小但容易模糊；&lt;/li>
&lt;li>&lt;strong>WebP&lt;/strong>：压缩率更高，但存在兼容性顾虑。&lt;/li>
&lt;/ul>
&lt;h4 class="heading" id="几种优化方式">
 几种优化方式&lt;span class="heading__anchor"> &lt;a href="#%e5%87%a0%e7%a7%8d%e4%bc%98%e5%8c%96%e6%96%b9%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h4>&lt;ol>
&lt;li>&lt;strong>算法压缩（TinyPNG、pngquant 等）&lt;/strong>
不影响质量，能显著减小体积。&lt;/li>
&lt;li>&lt;strong>降低质量&lt;/strong>
减小体积，但大图可能会糊。&lt;/li>
&lt;li>&lt;strong>改用 WebP&lt;/strong>
压缩率高，但部分旧系统仍有兼容风险。&lt;/li>
&lt;/ol>
&lt;p>三种方式中，第一种几乎没有副作用。
综合稳定性和收益，我选择了算法压缩。&lt;/p>
&lt;p>我用 &lt;strong>TinyPNG&lt;/strong> 来处理图片。
它的效果不错，但免费账号有调用限制，只能一次次通过网页上传压缩。&lt;/p>
&lt;p>压缩前后效果对比：&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/jyRcbh4.png">&lt;/figure>&lt;p>从 32.5MB 缩减至 16.9MB，缩减 &lt;strong>48%&lt;/strong> 。&lt;/p>
&lt;h3 class="heading" id="代码">
 代码&lt;span class="heading__anchor"> &lt;a href="#%e4%bb%a3%e7%a0%81">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>代码层面最常见的方式是 &lt;strong>混淆&lt;/strong>。
混淆不仅能减小体积，也能提高反编译难度，可谓一举两得。&lt;/p>
&lt;p>配置也很简单：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gradle" data-lang="gradle">&lt;span class="line">&lt;span class="cl">&lt;span class="n">isMinifyEnabled&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>大多数项目都会默认开启，这里不再赘述。&lt;/p>
&lt;h2 class="heading" id="实操">
 实操&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%9e%e6%93%8d">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>当前阶段的优化主要是：&lt;/p>
&lt;ul>
&lt;li>过滤 ABI，只保留 &lt;code>arm64-v8a&lt;/code>；&lt;/li>
&lt;li>图片压缩；&lt;/li>
&lt;li>确认开启代码混淆。&lt;/li>
&lt;/ul>
&lt;p>这些操作后，应用从 &lt;strong>140.3MB → 58.3MB&lt;/strong>，整体缩减 &lt;strong>58%&lt;/strong> ，效果明显。&lt;/p>
&lt;h2 class="heading" id="回顾">
 回顾&lt;span class="heading__anchor"> &lt;a href="#%e5%9b%9e%e9%a1%be">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>上面这些是最基础、最常规的优化，不需要额外依赖，也不会影响功能。
除此之外，还可以考虑以下两种更深入的做法：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>资源动态下发&lt;/strong>
除启动图、占位图、错误图外，其他资源可由服务端下发。
优点是包更轻，缺点是首启依赖网络。&lt;/li>
&lt;li>&lt;strong>插件化 / 模块拆分&lt;/strong>
只保留首页等必要模块，把购买、订单、播放等拆成独立 apk 以插件形式按需下载。
效果好，但工程量大。&lt;/li>
&lt;/ol>
&lt;h3 class="heading" id="防劣">
 防劣&lt;span class="heading__anchor"> &lt;a href="#%e9%98%b2%e5%8a%a3">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>ABI 配置一般不会变，但图片最容易劣化。
随着业务迭代，不断有新图片被引入。
为了防止包体积回涨，最好不要依赖人工检查，而是让持续集成自动化完成。&lt;/p>
&lt;p>可以这样做：&lt;/p>
&lt;ul>
&lt;li>构建阶段批量压缩图片；&lt;/li>
&lt;li>只处理新增文件（或将压缩文件直接提交到 Git 仓库）；&lt;/li>
&lt;li>定期记录包体积，若超过阈值（例如 +5MB）则触发警告。&lt;/li>
&lt;/ul>
&lt;p>这样既能保证体积稳定，又不会明显增加构建时间。&lt;/p></description></item><item><title>理解 Kotlin Flow：冷热流与背压处理</title><link>https://opoojkk.com/posts/2025/10/16/understanding-kotlin-flow/</link><pubDate>Thu, 16 Oct 2025 07:32:28 +0800</pubDate><guid>https://opoojkk.com/posts/2025/10/16/understanding-kotlin-flow/</guid><description>&lt;h2 class="heading" id="使用场景">
 使用场景&lt;span class="heading__anchor"> &lt;a href="#%e4%bd%bf%e7%94%a8%e5%9c%ba%e6%99%af">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="1-持续输出数据如定时器进度条">
 1. 持续输出数据（如定时器、进度条）&lt;span class="heading__anchor"> &lt;a href="#1-%e6%8c%81%e7%bb%ad%e8%be%93%e5%87%ba%e6%95%b0%e6%8d%ae%e5%a6%82%e5%ae%9a%e6%97%b6%e5%99%a8%e8%bf%9b%e5%ba%a6%e6%9d%a1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">timerFlow&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">Flow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Int&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">flow&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="m">1.&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="m">5&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">delay&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1000&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// 每秒发出一个进度
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">runBlocking&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">timerFlow&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">onEach&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Progress: &lt;/span>&lt;span class="si">$it&lt;/span>&lt;span class="s2">/5&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">onCompletion&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Done!&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">collect&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 输出：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Progress: 1/5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Progress: 2/5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Progress: 3/5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Progress: 4/5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Progress: 5/5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Done!
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="2-在不同线程中执行">
 2. 在不同线程中执行&lt;span class="heading__anchor"> &lt;a href="#2-%e5%9c%a8%e4%b8%8d%e5%90%8c%e7%ba%bf%e7%a8%8b%e4%b8%ad%e6%89%a7%e8%a1%8c">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">fetchData&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">Flow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">flow&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Running in thread: &lt;/span>&lt;span class="si">${Thread.currentThread().name}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">delay&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1000&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Data from network&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}.&lt;/span>&lt;span class="n">flowOn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">Dispatchers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">IO&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// 在 IO 线程执行
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">runBlocking&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetchData&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">onEach&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Collect on thread: &lt;/span>&lt;span class="si">${Thread.currentThread().name}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">collect&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Received: &lt;/span>&lt;span class="si">$it&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 输出：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Running in thread: DefaultDispatcher-worker-1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Collect on thread: main
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Received: Data from network
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="3-合并多个异步请求">
 3. 合并多个异步请求&lt;span class="heading__anchor"> &lt;a href="#3-%e5%90%88%e5%b9%b6%e5%a4%9a%e4%b8%aa%e5%bc%82%e6%ad%a5%e8%af%b7%e6%b1%82">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">getUser&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">Flow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">flow&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">delay&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">500&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;User: Alice&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">getMessagesCount&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">Flow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Int&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">flow&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">delay&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">800&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">42&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">runBlocking&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">combine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">getUser&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">getMessagesCount&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">user&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">count&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">$user&lt;/span>&lt;span class="s2">, Unread messages: &lt;/span>&lt;span class="si">$count&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}.&lt;/span>&lt;span class="n">collect&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 输出：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// User: Alice, Unread messages: 42
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这些特性让人想起了 RxJava&amp;hellip;&lt;/p>
&lt;h2 class="heading" id="冷热流">
 冷热流&lt;span class="heading__anchor"> &lt;a href="#%e5%86%b7%e7%83%ad%e6%b5%81">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Flow 官方文档中区分为&lt;strong>冷流&lt;/strong>和&lt;strong>热流&lt;/strong>两种类型，指的是数据的发送特性。&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>类型&lt;/th>
 &lt;th>生活类比&lt;/th>
 &lt;th>特征&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>冷流 (Cold Flow)&lt;/strong>&lt;/td>
 &lt;td>&lt;strong>点外卖&lt;/strong>：每次点餐，餐厅才开始做菜&lt;/td>
 &lt;td>每次订阅（collect）都会重新触发一次数据生产&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>热流 (Hot Flow)&lt;/strong>&lt;/td>
 &lt;td>&lt;strong>看直播&lt;/strong>：主播持续直播，新进来的观众只能看到当前及之后的内容&lt;/td>
 &lt;td>数据持续发送，不会重放过去的数据&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>另一个比喻是将 Flow 看作一条传送带：冷流是按需求启动传送，将所有商品传送给你；热流则是始终在运行，你只能捕获你到达时之后的商品。&lt;/p>
&lt;h2 class="heading" id="实现原理">
 实现原理&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%9e%e7%8e%b0%e5%8e%9f%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="flow-的基础接口">
 Flow 的基础接口&lt;span class="heading__anchor"> &lt;a href="#flow-%e7%9a%84%e5%9f%ba%e7%a1%80%e6%8e%a5%e5%8f%a3">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Flow 是一个接口，只定义了一个方法用于收集数据。&lt;code>FlowCollector&lt;/code> 负责发送数据：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Flow.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">public&lt;/span> &lt;span class="k">interface&lt;/span> &lt;span class="nc">Flow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="k">out&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">suspend&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">collect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">collector&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">FlowCollector&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// FlowCollector.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">interface&lt;/span> &lt;span class="n">FlowCollector&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="k">in&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">suspend&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">value&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="冷流的执行流程">
 冷流的执行流程&lt;span class="heading__anchor"> &lt;a href="#%e5%86%b7%e6%b5%81%e7%9a%84%e6%89%a7%e8%a1%8c%e6%b5%81%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>以下面的代码为例：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">CoroutineScope&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Job&lt;/span>&lt;span class="p">()).&lt;/span>&lt;span class="n">launch&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">flowOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">3&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">collect&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>冷流的发送分为两个步骤：定义数据生产逻辑，然后触发数据收集。&lt;/p>
&lt;p>代码实际上执行了 &lt;code>unsafeFlow&lt;/code> 方法：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">internal&lt;/span> &lt;span class="k">inline&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">unsafeFlow&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@BuilderInference&lt;/span> &lt;span class="k">crossinline&lt;/span> &lt;span class="n">block&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">suspend&lt;/span> &lt;span class="n">FlowCollector&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;.()&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Unit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">):&lt;/span> &lt;span class="n">Flow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">object&lt;/span> &lt;span class="err">: &lt;/span>&lt;span class="nc">Flow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">suspend&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">collect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">collector&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">FlowCollector&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">collector&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">block&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>其核心逻辑是：使用 &lt;code>FlowCollector&lt;/code> 执行外部传入的 &lt;code>block&lt;/code>。&lt;code>FlowCollector&lt;/code> 来自哪里？正是 &lt;code>collect()&lt;/code> 方法参数传入的对象。由于 &lt;code>FlowCollector&lt;/code> 是函数式接口，可以直接写成 &lt;code>collect { ... }&lt;/code> 的形式。&lt;/p>
&lt;p>&lt;strong>小结&lt;/strong>：冷流先定义要让某个 &lt;code>FlowCollector&lt;/code> 做什么事，再通过 &lt;code>collect()&lt;/code> 找到具体执行者。这就是为什么只有调用 &lt;code>collect()&lt;/code> 才会收集到所有数据。&lt;/p>
&lt;h3 class="heading" id="热流sharedflow-和-stateflow">
 热流：SharedFlow 和 StateFlow&lt;span class="heading__anchor"> &lt;a href="#%e7%83%ad%e6%b5%81sharedflow-%e5%92%8c-stateflow">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>热流包括 &lt;code>StateFlow&lt;/code> 和 &lt;code>SharedFlow&lt;/code> 两种，其中 &lt;code>StateFlow&lt;/code> 是 &lt;code>SharedFlow&lt;/code> 的子类。不妨先理解 &lt;code>SharedFlow&lt;/code>，再看 &lt;code>StateFlow&lt;/code> 会更清晰。&lt;/p>
&lt;p>热流的特点是持续发送数据，不关心是否有订阅者。示例如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">runBlocking&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 创建一个 SharedFlow，支持多个订阅者
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">eventFlow&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">MutableSharedFlow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">&amp;gt;()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 启动两个收集者
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">launch&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">eventFlow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">collect&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">value&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;收集者A 收到事件：&lt;/span>&lt;span class="si">$value&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">launch&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">eventFlow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">collect&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">value&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;收集者B 收到事件：&lt;/span>&lt;span class="si">$value&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 模拟发送事件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">delay&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">100&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">eventFlow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;用户点击按钮&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">eventFlow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;加载完成&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">delay&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">500&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 输出：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 收集者A 收到事件：用户点击按钮
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 收集者B 收到事件：用户点击按钮
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 收集者A 收到事件：加载完成
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 收集者B 收到事件：加载完成
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>所有订阅者都会收到消息。默认情况下不会保留历史值，除非创建 &lt;code>SharedFlow&lt;/code> 时设置了 &lt;code>replay&lt;/code> 参数，将上一个值和最新值先后发送。&lt;/p>
&lt;h4 class="heading" id="缓冲溢出处理">
 缓冲溢出处理&lt;span class="heading__anchor"> &lt;a href="#%e7%bc%93%e5%86%b2%e6%ba%a2%e5%87%ba%e5%a4%84%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>&lt;code>SharedFlow&lt;/code> 内部实现了缓冲机制，通过 &lt;code>replay&lt;/code> 参数控制缓存容量。当缓冲区满时，通过 &lt;code>BufferOverflow&lt;/code> 枚举决定处理策略：&lt;/p>
&lt;ul>
&lt;li>&lt;code>SUSPEND&lt;/code>：挂起等待直到有消费者处理缓冲区数据&lt;/li>
&lt;li>&lt;code>DROP_OLDEST&lt;/code>：丢弃最早的数据&lt;/li>
&lt;li>&lt;code>DROP_LATEST&lt;/code>：丢弃最新的数据&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="emit-发送流程">
 emit 发送流程&lt;span class="heading__anchor"> &lt;a href="#emit-%e5%8f%91%e9%80%81%e6%b5%81%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>SharedFlow 的数据发送分为两个主要阶段：快速通道和常规通道。&lt;/p>
&lt;h3 class="heading" id="快速通道">
 快速通道&lt;span class="heading__anchor"> &lt;a href="#%e5%bf%ab%e9%80%9f%e9%80%9a%e9%81%93">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">tryEmitLocked&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">value&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">Boolean&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 快速路径：无订阅者 -&amp;gt; 无需缓冲
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">nCollectors&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">tryEmitNoCollectorsLocked&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 有订阅者，需要检查缓冲区
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">bufferSize&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="n">bufferCapacity&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">minCollectorIndex&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">replayIndex&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">when&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">onBufferOverflow&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">BufferOverflow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SUSPEND&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">false&lt;/span> &lt;span class="c1">// 需要挂起
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nc">BufferOverflow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DROP_LATEST&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">true&lt;/span> &lt;span class="c1">// 丢弃新值
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nc">BufferOverflow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DROP_OLDEST&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{}&lt;/span> &lt;span class="c1">// 强制加入队列并丢弃旧值
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">enqueueLocked&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bufferSize&lt;/span>&lt;span class="o">++&lt;/span> &lt;span class="c1">// 值已加入缓冲区
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 如果缓冲区超过容量，删除最旧的值
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">bufferSize&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">bufferCapacity&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">dropOldestLocked&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 保持 replaySize 不超过必要值
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">replaySize&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">replay&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">updateBufferLocked&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">replayIndex&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">minCollectorIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bufferEndIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">queueEndIndex&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>快速通道的条件包括：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>无订阅者&lt;/strong>：只需更新缓冲区，无需发送数据&lt;/li>
&lt;li>&lt;strong>有订阅者&lt;/strong>：
&lt;ul>
&lt;li>缓冲区未满：直接加入队列&lt;/li>
&lt;li>缓冲区已满且有未处理数据：
&lt;ul>
&lt;li>&lt;code>DROP_LATEST&lt;/code> 策略：直接丢弃新值&lt;/li>
&lt;li>&lt;code>DROP_OLDEST&lt;/code> 策略：新值加入，旧值被丢弃&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 class="heading" id="常规通道挂起等待">
 常规通道（挂起等待）&lt;span class="heading__anchor"> &lt;a href="#%e5%b8%b8%e8%a7%84%e9%80%9a%e9%81%93%e6%8c%82%e8%b5%b7%e7%ad%89%e5%be%85">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>当无法走快速通道（缓冲区满且策略为 &lt;code>SUSPEND&lt;/code>）时，系统会：&lt;/p>
&lt;ol>
&lt;li>再次检查是否满足快速通道条件&lt;/li>
&lt;li>若仍不满足，将发送操作封装为 &lt;code>CancellableContinuation&lt;/code>&lt;/li>
&lt;li>通过协程状态机机制，待订阅者消费后唤醒该操作&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="collect-收集流程">
 collect 收集流程&lt;span class="heading__anchor"> &lt;a href="#collect-%e6%94%b6%e9%9b%86%e6%b5%81%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;code>collect()&lt;/code> 时会将每个订阅者封装成 &lt;code>AbstractSharedFlowSlot&lt;/code>。Slot 记录：&lt;/p>
&lt;ul>
&lt;li>订阅者的 &lt;code>Continuation&lt;/code>（协程中断点）&lt;/li>
&lt;li>当前收集到的缓冲数据索引&lt;/li>
&lt;/ul>
&lt;p>执行流程如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">collect()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ allocateSlot() // 为当前订阅者分配 slot，跟踪其读取进度
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ 无限循环处理数据：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ tryTakeValue(slot) // 尝试快速取值（非阻塞）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ ├─ 缓冲区有新值 → 立即返回该值
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ └─ 缓冲区无新值 → awaitValue(slot) 挂起等待
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ ensureActive() // 检查协程是否被取消
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ emit(value) // 将值发送给下游
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─ finally → freeSlot() // 收集完成或异常时释放 slot
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="stateflow">
 StateFlow&lt;span class="heading__anchor"> &lt;a href="#stateflow">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;code>StateFlow&lt;/code> 是 &lt;code>SharedFlow&lt;/code> 的特殊版本，主要区别：&lt;/p>
&lt;ul>
&lt;li>增加了 &lt;code>value&lt;/code> 属性存储当前状态&lt;/li>
&lt;li>禁止修改 &lt;code>replay&lt;/code> 参数，只能缓存一个值&lt;/li>
&lt;li>内部逻辑简化为纯粹的状态更新&lt;/li>
&lt;/ul>
&lt;p>每次调用 &lt;code>emit()&lt;/code> 都会触发状态更新，订阅者同样被封装成 slot。状态更新通过 CAS（Compare-And-Set）原子操作实现：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">makePending&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">loop&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">state&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">when&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">state&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">null&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="c1">// slot 已释放，跳过
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">state&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="n">PENDING&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="c1">// 已标记为待发送，无需操作
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">state&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="n">NONE&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="c1">// 标记为待发送
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">_state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">compareAndSet&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">PENDING&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="c1">// slot 中存储着挂起的 Continuation
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">_state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">compareAndSet&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">NONE&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">CancellableContinuationImpl&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Unit&lt;/span>&lt;span class="p">&amp;gt;).&lt;/span>&lt;span class="n">resume&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Unit&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>StateFlow&lt;/code> 的 &lt;code>collect()&lt;/code> 比 &lt;code>SharedFlow&lt;/code> 简化，因为只需处理单个值的更新，无需考虑 replay 机制。&lt;/p>
&lt;h2 class="heading" id="背压backpressure">
 背压（Backpressure）&lt;span class="heading__anchor"> &lt;a href="#%e8%83%8c%e5%8e%8bbackpressure">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="背压是什么">
 背压是什么&lt;span class="heading__anchor"> &lt;a href="#%e8%83%8c%e5%8e%8b%e6%98%af%e4%bb%80%e4%b9%88">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>“背压”指的是上游发射数据的速度超过下游处理能力导致的积压问题。
在 Flow 中，背压常见于：&lt;/p>
&lt;ul>
&lt;li>热流：数据持续产生，下游来不及消费。&lt;/li>
&lt;li>冷流：虽然每次重新生产，但如果中间操作符不处理，也可能积压。&lt;/li>
&lt;/ul>
&lt;h3 class="heading" id="flow-的内建背压挂起机制">
 Flow 的内建背压：挂起机制&lt;span class="heading__anchor"> &lt;a href="#flow-%e7%9a%84%e5%86%85%e5%bb%ba%e8%83%8c%e5%8e%8b%e6%8c%82%e8%b5%b7%e6%9c%ba%e5%88%b6">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Flow 的 &lt;code>emit()&lt;/code> 是挂起函数，如果下游还没处理完，上游会自动挂起，不会像 RxJava 那样“暴走式”地发：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">flow&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">repeat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1000&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// 下游慢的话，这里会挂起
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}.&lt;/span>&lt;span class="n">collect&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">delay&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">100&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// 模拟慢消费
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这就是 Flow 的“自带背压控制”：天然限速。&lt;/p>
&lt;h3 class="heading" id="sharedflow-的背压策略bufferoverflow">
 SharedFlow 的背压策略：BufferOverflow&lt;span class="heading__anchor"> &lt;a href="#sharedflow-%e7%9a%84%e8%83%8c%e5%8e%8b%e7%ad%96%e7%95%a5bufferoverflow">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>热流场景下，&lt;code>SharedFlow&lt;/code> 的背压控制靠 &lt;code>BufferOverflow&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">flow&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">MutableSharedFlow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Int&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">replay&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">extraBufferCapacity&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">onBufferOverflow&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nc">BufferOverflow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DROP_OLDEST&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>策略&lt;/th>
 &lt;th>行为&lt;/th>
 &lt;th>适用场景&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>SUSPEND&lt;/code>&lt;/td>
 &lt;td>挂起发送者等待消费完成&lt;/td>
 &lt;td>对数据完整性要求高&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>DROP_OLDEST&lt;/code>&lt;/td>
 &lt;td>丢掉最老的值，保留最新&lt;/td>
 &lt;td>UI 状态、最新数据为准&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>DROP_LATEST&lt;/code>&lt;/td>
 &lt;td>丢掉当前值，保留已有&lt;/td>
 &lt;td>已有值重要性更高&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;h3 class="heading" id="冷流的外部背压操作符控制">
 冷流的外部背压：操作符控制&lt;span class="heading__anchor"> &lt;a href="#%e5%86%b7%e6%b5%81%e7%9a%84%e5%a4%96%e9%83%a8%e8%83%8c%e5%8e%8b%e6%93%8d%e4%bd%9c%e7%ac%a6%e6%8e%a7%e5%88%b6">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>冷流本身挂起就能控制背压，但还能通过操作符增强控制：&lt;/p>
&lt;ul>
&lt;li>&lt;code>buffer()&lt;/code>：增加缓冲区，让生产者不被挂起。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">flow&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">repeat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1000&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">100&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="n">collect&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">delay&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">100&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;code>conflate()&lt;/code>：丢弃中间值，只保留最新。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">flow&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">repeat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1000&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}.&lt;/span>&lt;span class="n">conflate&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="n">collect&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">delay&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">100&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;code>collectLatest()&lt;/code>：取消未完成的处理，只处理最新值。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">flow&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">repeat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1000&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}.&lt;/span>&lt;span class="n">collectLatest&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">value&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">delay&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">100&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Processing &lt;/span>&lt;span class="si">$value&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这些操作符本质上是“外部背压策略”，和 SharedFlow 的内部策略互补。&lt;/p>
&lt;h2 class="heading" id="总结flow-的设计哲学与实战建议">
 总结：Flow 的设计哲学与实战建议&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93flow-%e7%9a%84%e8%ae%be%e8%ae%a1%e5%93%b2%e5%ad%a6%e4%b8%8e%e5%ae%9e%e6%88%98%e5%bb%ba%e8%ae%ae">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;code>Flow&lt;/code> 的设计哲学可以用一句话概括：“用挂起取代背压，用语义取代样板。”
它不像 RxJava 那样通过复杂的背压策略来管理数据流速，而是直接通过 &lt;code>suspend&lt;/code> 的天然特性让上游“等一等”下游，简化了响应式流的心智负担。同时，Flow 的 API 更接近 Kotlin 本身的语义风格，与协程深度融合，让异步代码更直观、更易读。&lt;/p>
&lt;p>在实际项目中：&lt;/p>
&lt;ul>
&lt;li>数据有限、按需生产时优先用冷流，例如网络请求结果、数据库查询等场景；&lt;/li>
&lt;li>数据持续产生、需要广播时使用热流，例如 UI 状态同步、事件分发；&lt;/li>
&lt;li>涉及频繁事件时关注背压，灵活使用 &lt;code>buffer()&lt;/code>、&lt;code>conflate()&lt;/code>、&lt;code>collectLatest()&lt;/code> 或 &lt;code>BufferOverflow&lt;/code> 策略，避免性能问题；&lt;/li>
&lt;li>复杂流组合场景下善用操作符（如 &lt;code>combine&lt;/code>、&lt;code>flatMapLatest&lt;/code>），可以极大简化数据流处理逻辑。&lt;/li>
&lt;/ul>
&lt;p>Flow 并不是 RxJava 的“轻量替代品”，而是站在协程语义之上的一套响应式思想实现。
理解了它的背压机制和冷热流模型之后，才可能用它构建出既优雅又高效的数据流管道。&lt;/p></description></item><item><title>Kotlin 编译器插件（译）</title><link>https://opoojkk.com/posts/2025/10/13/ak-compiler-plugin/</link><pubDate>Mon, 13 Oct 2025 21:09:56 +0800</pubDate><guid>https://opoojkk.com/posts/2025/10/13/ak-compiler-plugin/</guid><description>&lt;blockquote>
&lt;p>原文：https://kt.academy/article/ak-compiler-plugin&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>这是&lt;a href="https://kt.academy/book/advanced_kotlin">《Advanced Kotlin》&lt;/a>一书的一个章节。您可以在 &lt;a href="https://leanpub.com/advanced_kotlin/">LeanPub&lt;/a> 或 &lt;a href="https://www.amazon.com/Advanced-Kotlin-Developers-Marcin-Moskala/dp/839668474X/">Amazon&lt;/a> 上找到它。&lt;/p>
&lt;/blockquote>
&lt;p>Kotlin 编译器是一个用于编译 Kotlin 代码的程序，同时也被 IDE 用于提供代码补全、警告等分析功能。与许多程序一样，Kotlin 编译器可以使用插件来改变其行为。我们通过扩展一个名为扩展（extension）的特殊类来定义 Kotlin 编译器插件，然后使用注册器（registrar）来注册它。每个扩展在编译器工作的特定阶段被调用，从而可能改变该阶段的结果。例如，你可以注册一个插件，当编译器为类生成父类型时被调用，从而向结果添加额外的父类型。当我们编写编译器插件时，我们受限于所支持的扩展允许我们做的事情。我们很快会讨论当前可用的扩展，但让我们先从一些关于编译器如何工作的基础知识开始。&lt;/p>
&lt;h2 class="heading" id="编译器前端和后端">
 编译器前端和后端&lt;span class="heading__anchor"> &lt;a href="#%e7%bc%96%e8%af%91%e5%99%a8%e5%89%8d%e7%ab%af%e5%92%8c%e5%90%8e%e7%ab%af">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Kotlin 是一种多平台语言，这意味着相同的代码可以用于为不同平台生成底层代码。合理的是，Kotlin 编译器被分为两大部分：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>前端（Frontend）&lt;/strong> ，负责解析和转换 Kotlin 代码为可以被后端解释并用于 Kotlin 代码分析的表示形式。&lt;/li>
&lt;li>&lt;strong>后端（Backend）&lt;/strong> ，负责基于从前端接收的表示形式生成实际的底层代码。&lt;/li>
&lt;/ul>
&lt;p>编译器前端与目标平台无关，当我们编译多平台模块时，其结果可以被重用。然而，目前正在发生一场革命，因为新的 K2 前端正在取代旧的 K1 前端。&lt;/p>
&lt;p>编译器后端是特定于编译目标的，所以 JVM、JS、Native 和 WASM 都有各自独立的后端。它们有一些共享的部分，但本质上是不同的。&lt;/p>
&lt;figure>
 &lt;img src="https://kt.academy/_next/image?url=https%3A%2F%2Fmarcinmoskala.com%2Fadvanced-kotlin-book%2Fmanuscript%2Fresources%2Fcompiler_frontend_backend.png&amp;amp;w=1080&amp;amp;q=75">&lt;/figure>&lt;p>编译器前端负责解析和分析 Kotlin 代码，并将其转换为发送给后端的表示形式，后端基于此生成平台特定的文件。前端与目标平台无关，但有两个前端：旧的 K1 和新的 K2。后端是特定于目标平台的。&lt;/p>
&lt;p>当你在像 IntelliJ 这样的 IDE 中使用 Kotlin 时，IDE 会向你显示警告、错误、组件使用情况、代码补全等，但 IntelliJ 本身并不分析 Kotlin：所有这些功能都基于与 Kotlin 编译器的通信，编译器有一个专门的 IDE API，而前端负责这种通信。&lt;/p>
&lt;p>每个后端变体都共享一部分，该部分从前端提供的表示形式生成 Kotlin 中间表示（在 K2 的情况下是 FIR，即前端中间表示）。平台特定的文件是基于这个表示形式生成的。&lt;/p>
&lt;figure>
 &lt;img src="https://kt.academy/_next/image?url=https%3A%2F%2Fmarcinmoskala.com%2Fadvanced-kotlin-book%2Fmanuscript%2Fresources%2Fcompiler_bckend_ir.png&amp;amp;w=1080&amp;amp;q=75">&lt;/figure>&lt;p>每个后端都共享一部分，该部分将前端提供的表示形式转换为 Kotlin 中间表示，该表示用于生成特定目标的文件。&lt;/p>
&lt;p>你可以在许多演示文稿和文章中找到关于编译器前端和编译器后端如何工作的详细描述，例如 Amanda Hinchman-Dominguez 或 Mikhail Glukhikh 的文章。我不会在这里详细讨论，因为我们已经涵盖了讨论编译器插件所需的一切。&lt;/p>
&lt;h2 class="heading" id="编译器扩展">
 编译器扩展&lt;span class="heading__anchor"> &lt;a href="#%e7%bc%96%e8%af%91%e5%99%a8%e6%89%a9%e5%b1%95">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Kotlin 编译器扩展也分为前端扩展和后端扩展。所有前端扩展都以 &lt;code>Fir&lt;/code> 前缀开头，以 &lt;code>Extension&lt;/code> 后缀结尾。以下是当前支持的 K2 扩展的完整列表&lt;a href="https://claude.ai/chat/35f77a94-7020-47c2-8dab-17bdbc5c0cc8#%E5%AE%9A%E4%B9%89-1">1&lt;/a>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>FirStatusTransformerExtension&lt;/strong> - 在元素状态（可见性、修饰符等）建立时被调用，允许对其进行更改。All-open 编译器插件使用它使所有带有适当注解的类默认为 open（例如，Spring Framework 使用）。&lt;/li>
&lt;li>&lt;strong>FirDeclarationGenerationExtension&lt;/strong> - 可以指定为 Kotlin 文件生成的额外声明。其不同的方法在编译的不同阶段被调用，允许生成不同类型的元素，如类或方法。被许多插件使用，包括 Kotlin Serialization 插件，用于生成序列化方法。&lt;/li>
&lt;li>&lt;strong>FirAdditionalCheckersExtension&lt;/strong> - 允许指定额外的检查器，这些检查器将在编译器检查代码时被调用；它还可以报告可以由 IntelliJ 可视化的额外错误或警告。&lt;/li>
&lt;li>&lt;strong>FirSupertypeGenerationExtension&lt;/strong> - 在编译器为类生成父类型时被调用，允许添加额外的父类型。例如，如果类 &lt;code>A&lt;/code> 继承自 &lt;code>B&lt;/code> 并实现 &lt;code>C&lt;/code>，扩展决定它还应该有父类型 &lt;code>D&lt;/code> 和 &lt;code>F&lt;/code>，那么编译器将认为 &lt;code>A&lt;/code> 有父类型 &lt;code>B&lt;/code>、&lt;code>C&lt;/code>、&lt;code>D&lt;/code> 和 &lt;code>F&lt;/code>。被许多插件使用，包括 Kotlin Serialization 插件，它使用此扩展使所有带有 &lt;code>Serializer&lt;/code> 注解的类具有带有适当类型参数的隐式 &lt;code>KSerializer&lt;/code> 父类型。&lt;/li>
&lt;li>&lt;strong>FirTypeAttributeExtension&lt;/strong> - 允许基于注解向类型添加属性，或基于属性确定注解。被实验性的 Kotlin Assignment 插件使用，该插件允许将数字类型注解为正数或负数，然后使用此信息在违反此约定时抛出错误。适用于我们项目使用的库代码。&lt;/li>
&lt;li>&lt;strong>FirExpressionResolutionExtension&lt;/strong> - 可用于在调用函数时添加隐式扩展接收者。被实验性的 Kotlin Assignment 插件使用，当调用 &lt;code>injectAlgebra&amp;lt;T&amp;gt;()&lt;/code> 时，它注入 &lt;code>Algebra&amp;lt;T&amp;gt;&lt;/code> 作为隐式接收者。&lt;/li>
&lt;li>&lt;strong>FirSamConversionTransformerExtension&lt;/strong> - 在编译器将 Java SAM 接口转换为 Kotlin 函数类型时被调用，允许更改结果类型。被 &lt;code>SAM-with-receiver 编译器插件&lt;/code> 使用，为带有适当注解的 SAM 接口生成带有接收者的函数类型而不是常规函数类型。&lt;/li>
&lt;li>&lt;strong>FirAssignExpressionAltererExtension&lt;/strong> - 允许将变量赋值转换为任何类型的语句。被实验性的 Kotlin Assignment 插件使用，该插件允许重载赋值运算符。&lt;/li>
&lt;li>&lt;strong>FirFunctionTypeKindExtension&lt;/strong> - 允许注册额外的函数类型。适用于我们项目使用的库代码。&lt;/li>
&lt;li>&lt;strong>FirDeclarationsForMetadataProviderExtension&lt;/strong> - 目前允许在 Kotlin 元数据中添加额外的声明。被 Kotlin Serialization 插件使用，用于生成反序列化构造函数或写入自身的方法。其行为在未来可能会改变。&lt;/li>
&lt;li>&lt;strong>FirScriptConfiguratorExtension&lt;/strong> - 目前在编译器处理脚本时被调用；它还允许更改脚本配置。其行为在未来可能会改变。&lt;/li>
&lt;li>&lt;strong>FirExtensionSessionComponent&lt;/strong> - 目前允许为会话添加额外的扩展会话组件。换句话说，它允许注册一个组件，以便可以被不同的扩展重用。被许多插件使用。例如，Kotlin Serialization 插件使用它来注册一个组件，该组件在文件或 KClass 首先从文件注解中保存序列化器的缓存。其行为在未来可能会改变。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>注意！&lt;/strong> 在本章中，我们只讨论 K2 前端扩展，因为 K1 前端已被弃用，将来会被移除。然而，K2 编译器前端目前默认不被使用。要使用它，你需要至少有 Kotlin 版本 1.9.0-Beta，并添加 &lt;code>-Pkotlin.experimental.tryK2=true&lt;/code> 编译器选项。&lt;/p>
&lt;p>如你所见，这些插件允许我们对编译和分析应用更改。它们可以用于显示警告或中断编译并报错。它们还可以用于更改特定元素的可见性，从而影响生成代码的行为和 IDE 中的建议。&lt;/p>
&lt;p>关于后端，只有一个扩展：&lt;code>IrGenerationExtension&lt;/code>。它在从 FIR（前端中间表示）生成 IR（Kotlin 中间表示）之后但在用于生成平台特定文件之前使用。&lt;code>IrGenerationExtension&lt;/code> 用于修改 IR 树。这意味着 &lt;code>IrGenerationExtension&lt;/code> 可以更改生成代码中的任何内容，但使用它很困难，因为我们很容易引入破坏性更改，所以必须非常小心地使用它。此外，&lt;code>IrGenerationExtension&lt;/code> 无法影响代码分析，因此无法影响 IDE 建议、警告等。&lt;/p>
&lt;figure>
 &lt;img src="https://kt.academy/_next/image?url=https%3A%2F%2Fmarcinmoskala.com%2Fadvanced-kotlin-book%2Fmanuscript%2Fresources%2Fbackend_plugin_place.png&amp;amp;w=1080&amp;amp;q=75">&lt;/figure>&lt;p>后端插件扩展在从 FIR（前端中间表示）生成 IR（Kotlin 中间表示）之后但在用于生成平台特定文件之前使用。&lt;/p>
&lt;p>我想明确一点，后端无法影响 IDE 分析。如果你使用 &lt;code>IrGenerationExtension&lt;/code> 向类添加方法，你将无法在 IntelliJ 中直接调用它，因为它无法识别这样的方法，所以你只能使用反射来调用它。相反，使用前端 &lt;code>FirDeclarationGenerationExtension&lt;/code> 添加到类的方法可以直接使用，因为 IDE 知道它的存在。&lt;/p>
&lt;p>大多数流行的 Kotlin 插件需要多个扩展，包括前端和后端。例如，Kotlin Serialization 使用后端扩展来生成所有序列化和反序列化的函数；另一方面，它使用前端扩展来添加隐式父类型、检查和声明。&lt;/p>
&lt;p>这是关于 Kotlin 编译器插件的基本知识。为了使其更实用一些，让我们看几个例子。&lt;/p>
&lt;h2 class="heading" id="流行的编译器插件">
 流行的编译器插件&lt;span class="heading__anchor"> &lt;a href="#%e6%b5%81%e8%a1%8c%e7%9a%84%e7%bc%96%e8%af%91%e5%99%a8%e6%8f%92%e4%bb%b6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>许多编译器插件和使用编译器插件的库已经可用。最流行的包括：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Kotlin Serialization&lt;/strong> - 一个为 Kotlin 类生成序列化方法的插件。它是多平台的，非常高效，因为它使用编译器插件而不是反射。&lt;/li>
&lt;li>&lt;strong>Jetpack Compose&lt;/strong> - 一个流行的 UI 框架，使用编译器插件来支持其视图元素定义。所有可组合函数都被转换为特殊的表示形式，然后由框架用于生成 UI。&lt;/li>
&lt;li>&lt;strong>Arrow Meta&lt;/strong> - 一个强大的插件，引入了函数式编程语言中已知的功能支持，如光学（optics）或精炼类型（refined types）。它还支持面向切面编程（Aspect Oriented Programming）。&lt;/li>
&lt;li>&lt;strong>Parcelize&lt;/strong> - 一个为 Kotlin 类生成 &lt;code>Parcelable&lt;/code> 实现的插件。它使用编译器插件向现有类添加适当的方法。&lt;/li>
&lt;li>&lt;strong>All-open&lt;/strong> - 一个使所有带有适当注解的类默认为 open 的插件。Spring Framework 使用它来使所有带有 &lt;code>@Component&lt;/code> 注解的类默认为 open（以便能够为它们创建代理）。&lt;/li>
&lt;/ul>
&lt;p>大多数插件使用多个扩展，所以让我们考虑简单的 Parcelize 插件，它只使用以下扩展：&lt;/p>
&lt;ul>
&lt;li>&lt;code>IrGenerationExtension&lt;/code> 来生成在底层使用的函数和属性。&lt;/li>
&lt;li>&lt;code>FirDeclarationGenerationExtension&lt;/code> 来生成项目编译所需的函数。&lt;/li>
&lt;li>&lt;code>FirAdditionalCheckersExtension&lt;/code> 来显示错误和警告。&lt;/li>
&lt;/ul>
&lt;p>Kotlin 编译器插件在 &lt;code>build.gradle(.kts)&lt;/code> 的 plugins 部分中定义：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">plugins&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;kotlin-parcelize&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>一些插件作为单独的 Gradle 插件的一部分分发。&lt;/p>
&lt;h2 class="heading" id="使所有类为-open">
 使所有类为 open&lt;span class="heading__anchor"> &lt;a href="#%e4%bd%bf%e6%89%80%e6%9c%89%e7%b1%bb%e4%b8%ba-open">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>我们将从一个简单的任务开始：使所有类为 open。这个行为受到 AllOpen 插件的启发，该插件打开所有带有指定注解之一的类。然而，我们的示例会更简单，因为我们只是打开所有类。&lt;/p>
&lt;p>作为依赖项，我们只需要 &lt;code>kotlin-compiler-embeddable&lt;/code>，它为我们提供了可用于定义插件的类。&lt;/p>
&lt;p>就像在 KSP 或注解处理中一样，我们需要在 &lt;code>resources/META-INF/services&lt;/code> 中添加一个带有注册器名称的文件。这个文件的名称应该是 &lt;code>org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar&lt;/code>，这是 &lt;code>CompilerPluginRegistrar&lt;/code> 类的完全限定名。在其中，你应该放置注册器类的完全限定名。在我们的例子中，这将是 &lt;code>com.marcinmoskala.AllOpenComponentRegistrar&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">// org.jetbrains.kotlin.compiler.plugin.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">// CompilerPluginRegistrar
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">com.marcinmoskala.AllOpenComponentRegistrar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>我们的 &lt;code>AllOpenComponentRegistrar&lt;/code> 注册器需要注册一个扩展注册器（我们称之为 &lt;code>FirAllOpenExtensionRegistrar&lt;/code>），该注册器注册我们的扩展。请注意，注册器可以访问配置，以便我们可以将参数传递给我们的插件，但我们现在不需要这个配置。我们的扩展只是一个扩展 &lt;code>FirStatusTransformerExtension&lt;/code> 的类；它有两个方法：&lt;code>needTransformStatus&lt;/code> 和 &lt;code>transformStatus&lt;/code>。前者确定是否应该应用转换；后者应用它。在我们的例子中，我们将扩展应用于所有类，并将它们的状态更改为 open，无论之前的状态是什么。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@file&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">OptIn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ExperimentalCompilerApi&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="k">class&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AllOpenComponentRegistrar&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">CompilerPluginRegistrar&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">ExtensionStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">registerExtensions&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configuration&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CompilerConfiguration&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">FirExtensionRegistrarAdapter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">registerExtension&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FirAllOpenExtensionRegistrar&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">supportsK2&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Boolean&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">get&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">FirAllOpenExtensionRegistrar&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">FirExtensionRegistrar&lt;/span>&lt;span class="p">(){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">ExtensionRegistrarContext&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">configurePlugin&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">+&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">FirAllOpenStatusTransformer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">FirAllOpenStatusTransformer&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">session&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">FirSession&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">FirStatusTransformerExtension&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">session&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">needTransformStatus&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">declaration&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">FirDeclaration&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">):&lt;/span> &lt;span class="n">Boolean&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">declaration&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">FirRegularClass&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">transformStatus&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">status&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">FirDeclarationStatus&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">declaration&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">FirDeclaration&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">):&lt;/span> &lt;span class="n">FirDeclarationStatus&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">status&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">transform&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">modality&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nc">Modality&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">OPEN&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这只是一个简化的版本，但实际的 AllOpen 插件稍微复杂一些，因为它只打开那些带有指定注解之一的类。为此，&lt;code>FirAllOpenExtensionRegistrar&lt;/code> 注册一个插件，该插件由 &lt;code>FirAllOpenStatusTransformer&lt;/code> 使用来确定是否应该打开特定的类。如果你对细节感兴趣，请查看 Kotlin 仓库中 &lt;code>plugins&lt;/code> 文件夹中的 AllOpen 插件。&lt;/p>
&lt;h2 class="heading" id="更改类型">
 更改类型&lt;span class="heading__anchor"> &lt;a href="#%e6%9b%b4%e6%94%b9%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>我们的下一个示例将是 SAM-with-receiver 编译器插件，它将从带有适当注解的 SAM 接口生成的函数类型更改为带有接收者的函数类型。它使用 &lt;code>FirSamConversionTransformerExtension&lt;/code>，这对于这个插件来说非常特定，因为它仅在 SAM 接口转换为函数类型时被调用，并且允许更改将要生成的类型。这个示例很有趣，因为它添加了一个类型，该类型将被 IDE 识别并可以直接在代码中使用。完整的实现可以在 Kotlin 仓库的 &lt;code>plugins/sam-with-receiver&lt;/code> 文件夹中找到，但这里我只想展示这个扩展的简化实现：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">FirScriptSamWithReceiverConventionTransformer&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">session&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">FirSession&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">FirSamConversionTransformerExtension&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">session&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">getCustomFunctionTypeForSamConversion&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">function&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">FirSimpleFunction&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">):&lt;/span> &lt;span class="n">ConeLookupTagBasedType&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">containingClassSymbol&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">function&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">containingClassLookupTag&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?.&lt;/span>&lt;span class="n">toFirRegularClassSymbol&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">session&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?:&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">shouldTransform&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">parameterTypes&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">valueParameters&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">map&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">it&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">returnTypeRef&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">coneType&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">parameterTypes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isEmpty&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">createFunctionType&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">getFunctionType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">parameters&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">parameterTypes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">drop&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">receiverType&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">parameterTypes&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rawReturnType&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">returnTypeRef&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">coneType&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如果 &lt;code>getCustomFunctionTypeForSamConversion&lt;/code> 函数不返回 &lt;code>null&lt;/code>，它将覆盖为 SAM 接口生成的类型。在我们的例子中，我们确定函数是否应该被转换；如果是，我们通过使用 &lt;code>createFunctionType&lt;/code> 函数创建一个带有接收者的函数类型。有构建器函数可以帮助我们创建在 FIR 中表示的许多元素。示例包括 &lt;code>buildSimpleFunction&lt;/code> 或 &lt;code>buildRegularClass&lt;/code>，它们中的大多数都提供了简单的 DSL。这里，&lt;code>createFunctionType&lt;/code> 函数创建一个类型为 &lt;code>ConeLookupTagBasedType&lt;/code> 的带有接收者表示的函数类型，它将替换从 SAM 接口自动生成的类型。本质上，这就是这个插件的工作原理。&lt;/p>
&lt;h2 class="heading" id="生成函数包装器">
 生成函数包装器&lt;span class="heading__anchor"> &lt;a href="#%e7%94%9f%e6%88%90%e5%87%bd%e6%95%b0%e5%8c%85%e8%a3%85%e5%99%a8">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>让我们考虑以下问题：Kotlin 挂起函数只能在 Kotlin 代码中调用。这意味着如果你想从 Java 调用挂起函数，你可以使用例如 &lt;code>runBlocking&lt;/code> 将其包装在一个常规函数中，该函数在协程中调用挂起函数。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">suspend&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">suspendFunction&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="o">..&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">blockingFunction&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">runBlocking&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">suspendFunction&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>我们可以使用插件来自动生成这样的挂起函数包装器，使用后端或前端插件。&lt;/p>
&lt;p>后端插件需要一个 &lt;code>IrGenerationExtension&lt;/code> 扩展，该扩展在 IR 中为适当的函数生成额外的包装函数。这些包装函数将存在于生成的平台特定代码中，因此可用于 Java、Groovy 和其他语言。问题是这些包装类在 Kotlin 代码中不可见。如果我们的包装函数本来就是要从其他语言使用的，这很好，但我们需要知道这个严重的限制。有一个名为 kotlin-jvm-blocking-bridge 的开源插件，它使用后端插件为挂起函数生成阻塞包装器；你可以在链接 github.com/Him188/kotlin-jvm-blocking-bridge 下找到其源代码。&lt;/p>
&lt;p>前端插件需要一个 &lt;code>FirDeclarationGenerationExtension&lt;/code> 类的扩展，为 FIR 中的适当挂起函数生成包装函数。然后，这些额外的函数将用于生成 IR，最后生成平台特定的代码。这些函数在 IntelliJ 中也可见，因此我们可以在 Kotlin 和 Java 中使用它们。然而，这样的插件只能与 K2 编译器一起工作，即从 Kotlin 2.0 开始。要支持以前的语言版本，我们需要定义一个支持 K1 的额外扩展。&lt;/p>
&lt;h2 class="heading" id="示例插件实现">
 示例插件实现&lt;span class="heading__anchor"> &lt;a href="#%e7%a4%ba%e4%be%8b%e6%8f%92%e4%bb%b6%e5%ae%9e%e7%8e%b0">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Kotlin 编译器插件目前没有文档，生成的元素必须遵守许多限制才能使我们的代码不会崩溃，所以定义自定义插件相当困难。如果你想定义自己的插件，我的建议是首先获取 Kotlin 编译器源代码，然后分析 &lt;code>plugins&lt;/code> 文件夹中的现有插件。&lt;/p>
&lt;figure>
 &lt;img src="https://kt.academy/_next/image?url=https%3A%2F%2Fmarcinmoskala.com%2Fadvanced-kotlin-book%2Fmanuscript%2Fresources%2Fplugins_folder.png&amp;amp;w=1080&amp;amp;q=75">&lt;/figure>&lt;p>这个文件夹不仅包括 K2 插件，还包括 K1 和基于 KSP 的插件。我们只对 K2 插件感兴趣，所以你可以忽略其余的。&lt;/p>
&lt;p>所有支持的扩展的列表可以在 &lt;code>FirExtensionRegistrar&lt;/code> 类中找到。要分析编译器如何使用扩展，你可以搜索其公开方法的使用情况。要做到这一点，按住 command/Ctrl 并点击方法名跳转到其使用位置。这应该向你显示 Kotlin 编译器在哪里使用这个扩展。但请注意，所有未记录的知识在未来更有可能改变。&lt;/p>
&lt;h2 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>如你所见，Kotlin 编译器插件的能力由 Kotlin 编译器支持的扩展决定。在编译器的前端，这些扩展能力是有限的，所以目前只有一组特定的事情可以在前端使用 Kotlin 编译器插件完成。在编译器后端，你可以以任何方式更改生成的 IR 表示；这提供了许多可能性，但也很容易在你的代码中引起破坏性更改。&lt;/p>
&lt;p>Kotlin 编译器插件技术仍然年轻、缺乏文档且在变化中。应该非常小心地使用它，因为它很容易破坏你的代码，但它也极其强大，提供了难以想象的可能性。Jetpack Compose 就是一个很好的例子。我只能与你分享 Kotlin 编译器插件如何工作以及它们能做什么的大致概念，但我希望这足以让你理解关键概念和可能性。&lt;/p>
&lt;p>在下一章中，我们将讨论另一个有助于代码开发的工具：静态代码分析器。一方面，它比 KSP 或编译器插件更有限，因为它无法生成任何代码；另一方面，静态代码分析器也极其强大，因为它们可以严重影响我们的开发过程并帮助我们改进实际代码。&lt;/p>
&lt;hr>
&lt;p>&lt;a href="https://claude.ai/chat/35f77a94-7020-47c2-8dab-17bdbc5c0cc8#%E4%BD%BF%E7%94%A8-1">1:&lt;/a> K1 扩展已被弃用，所以我将跳过它们。&lt;/p>
&lt;h2 class="heading" id="作者">
 作者&lt;span class="heading__anchor"> &lt;a href="#%e4%bd%9c%e8%80%85">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;a href="https://kt.academy/user/marcinmoskala">Marcin Moskała&lt;/a>&lt;/p>
&lt;p>Marcin Moskala 是一位经验丰富的开发者和 Kotlin 教师，同时也是 Kt. Academy 的创始人，该机构是 JetBrains 的官方合作伙伴，专注于 Kotlin 培训。他还是 Google Developers Expert，以其对 Kotlin 社区的重大贡献而闻名。Moskala 著有多本广受认可的书籍，包括《Effective Kotlin》、《Kotlin Coroutines》、《Functional Kotlin》、《Advanced Kotlin》、《Kotlin Essentials》和《Android Development with Kotlin》。&lt;/p>
&lt;p>除了在出版领域的成就外，Moskala 还是 Medium 上最大的 Kotlin 专栏的作者。作为一名备受尊敬的演讲者，他曾受邀在众多编程大会上分享见解，包括 Droidcon 以及 Kotlin 编程语言顶级大会 Kotlin Conf 等知名活动。&lt;/p></description></item><item><title>Kotlin 协程：Job 与 SupervisorJob 的差异</title><link>https://opoojkk.com/posts/2025/10/10/kotlin-job-vs-supervisorjob-source-analysis/</link><pubDate>Fri, 10 Oct 2025 21:13:33 +0800</pubDate><guid>https://opoojkk.com/posts/2025/10/10/kotlin-job-vs-supervisorjob-source-analysis/</guid><description>&lt;p>我们都知道，在 Kotlin 协程中，可以使用 &lt;code>Job&lt;/code> 或 &lt;code>SupervisorJob&lt;/code> 来构建一个作用域（&lt;code>CoroutineScope&lt;/code>）：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// SupervisorJob 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">val&lt;/span> &lt;span class="py">scope&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">CoroutineScope&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SupervisorJob&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">scope&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">launch&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">..&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Job
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">val&lt;/span> &lt;span class="py">scope&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">CoroutineScope&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Job&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">scope&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">launch&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">..&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>两者的区别似乎仅在于构造函数不同。点开源码一看，差别的确只有一个方法：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">Job&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Job&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">CompletableJob&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">JobImpl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">SupervisorJob&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Job&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">CompletableJob&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">SupervisorJobImpl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">SupervisorJobImpl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Job&lt;/span>&lt;span class="p">?)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">JobImpl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">childCancelled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Throwable&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">Boolean&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>SupervisorJobImpl&lt;/code> 继承自 &lt;code>JobImpl&lt;/code>，唯一做的事就是&lt;strong>重写了 &lt;code>childCancelled&lt;/code> 方法&lt;/strong>。
也就是说，整个差异的核心就在这里。&lt;/p>
&lt;h2 class="heading" id="jobimpl-的取消逻辑">
 JobImpl 的取消逻辑&lt;span class="heading__anchor"> &lt;a href="#jobimpl-%e7%9a%84%e5%8f%96%e6%b6%88%e9%80%bb%e8%be%91">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>来看 &lt;code>JobImpl&lt;/code> 的实现：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">open&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">childCancelled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Throwable&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">Boolean&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">CancellationException&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">cancelImpl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">handlesException&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当子协程异常时，&lt;code>JobImpl&lt;/code> 默认会执行 &lt;code>cancelImpl(cause)&lt;/code>，
也就是会触发父协程的取消逻辑。&lt;/p>
&lt;p>在 &lt;code>cancelImpl&lt;/code> 中：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">internal&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">cancelImpl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">?):&lt;/span> &lt;span class="n">Boolean&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">var&lt;/span> &lt;span class="py">finalState&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">COMPLETING_ALREADY&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">onCancelComplete&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">finalState&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">cancelMakeCompleting&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">finalState&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="n">COMPLETING_WAITING_CHILDREN&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">finalState&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="n">COMPLETING_ALREADY&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 执行取消
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">finalState&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">makeCancelling&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">when&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">finalState&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="n">COMPLETING_ALREADY&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">finalState&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="n">COMPLETING_WAITING_CHILDREN&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">finalState&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="n">TOO_LATE_TO_CANCEL&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="k">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">afterCompletion&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">finalState&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>也就是说，一旦子协程抛出异常，&lt;code>JobImpl&lt;/code> 就会触发父协程的 &lt;code>makeCancelling()&lt;/code> 流程，
继而执行 &lt;code>notifyCancelling()&lt;/code>，从而&lt;strong>传播取消信号&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">notifyCancelling&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">list&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">NodeList&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cause&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Throwable&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 取消子任务
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">onCancelling&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">notifyHandlers&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">JobCancellingNode&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="n">list&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cause&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 取消父任务
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cancelParent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="supervisorjob-的不同">
 SupervisorJob 的不同&lt;span class="heading__anchor"> &lt;a href="#supervisorjob-%e7%9a%84%e4%b8%8d%e5%90%8c">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>而 &lt;code>SupervisorJobImpl&lt;/code> 的 &lt;code>childCancelled&lt;/code> 方法返回 &lt;code>false&lt;/code>。
这意味着：当子协程异常取消时，父协程&lt;strong>不会被取消&lt;/strong>。&lt;/p>
&lt;p>这一点正是 &lt;code>SupervisorJob&lt;/code> 的核心特性：&lt;/p>
&lt;blockquote>
&lt;p>子协程的失败不会影响到其他兄弟协程，也不会导致整个作用域取消。&lt;/p>
&lt;/blockquote>
&lt;h2 class="heading" id="childcancelled-的调用链">
 childCancelled 的调用链&lt;span class="heading__anchor"> &lt;a href="#childcancelled-%e7%9a%84%e8%b0%83%e7%94%a8%e9%93%be">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>继续往下看，在 &lt;code>ChildHandleNode&lt;/code> 中：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">ChildHandleNode&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@JvmField&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">childJob&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">ChildJob&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">JobNode&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">ChildHandle&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">parent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Job&lt;/span> &lt;span class="k">get&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">job&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">onCancelling&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Boolean&lt;/span> &lt;span class="k">get&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Throwable&lt;/span>&lt;span class="p">?)&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">childJob&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">parentCancelled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">job&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">childCancelled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Throwable&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">Boolean&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">job&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">childCancelled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里当子任务被取消时，会通过 &lt;code>childCancelled&lt;/code> 将异常传递回父任务。
而 &lt;code>SupervisorJobImpl&lt;/code> 正是通过重写 &lt;code>childCancelled&lt;/code> 来“截断”这个传播链。&lt;/p>
&lt;h2 class="heading" id="cancelparent-的触发点">
 cancelParent 的触发点&lt;span class="heading__anchor"> &lt;a href="#cancelparent-%e7%9a%84%e8%a7%a6%e5%8f%91%e7%82%b9">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>那么，&lt;code>cancelParent&lt;/code> 是在哪里被调用的？
在 &lt;code>JobSupport.kt&lt;/code> 中：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">cancelParent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Throwable&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">Boolean&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">isScopedCoroutine&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">isCancellation&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">cause&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">CancellationException&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">parent&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">parentHandle&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="k">null&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">parent&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="n">NonDisposableHandle&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">isCancellation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 通知父任务
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">parent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">childCancelled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">isCancellation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>从注释可以看出：&lt;/p>
&lt;blockquote>
&lt;p>对于正常的 &lt;code>CancellationException&lt;/code>，不会取消父任务；
只有当出现其他异常（例如崩溃）时，才可能向父任务传播。&lt;/p>
&lt;/blockquote>
&lt;p>这个方法会在 &lt;code>notifyCancelling()&lt;/code> 和 &lt;code>finalizeFinishingState()&lt;/code> 中被调用。
而后者正是任务完成状态的最终确定逻辑。&lt;/p>
&lt;h2 class="heading" id="finalizefinishingstate异常如何进入最终状态">
 finalizeFinishingState：异常如何进入“最终状态”&lt;span class="heading__anchor"> &lt;a href="#finalizefinishingstate%e5%bc%82%e5%b8%b8%e5%a6%82%e4%bd%95%e8%bf%9b%e5%85%a5%e6%9c%80%e7%bb%88%e7%8a%b6%e6%80%81">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在 &lt;code>finalizeFinishingState&lt;/code> 中：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">finalizeFinishingState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Finishing&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">proposedUpdate&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">?):&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">proposedException&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">proposedUpdate&lt;/span> &lt;span class="k">as&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="n">CompletedExceptionally&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">cause&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">finalException&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">synchronized&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">exceptions&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sealLocked&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">proposedException&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">finalCause&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">getFinalRootCause&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">exceptions&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">finalCause&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">addSuppressedExceptions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">finalCause&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">exceptions&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">finalCause&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">finalState&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">when&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">finalException&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">null&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">proposedUpdate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">finalException&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="n">proposedException&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">proposedUpdate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">CompletedExceptionally&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">finalException&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">finalException&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">handled&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">cancelParent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">finalException&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">handleJobException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">finalException&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">handled&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">finalState&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">CompletedExceptionally&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">makeHandled&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">completeStateFinalization&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">finalState&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">finalState&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里的关键在于：&lt;/p>
&lt;ul>
&lt;li>如果 &lt;code>cancelParent&lt;/code> 返回 &lt;code>true&lt;/code>，说明异常已被父协程处理；&lt;/li>
&lt;li>否则，&lt;code>handleJobException&lt;/code> 再尝试处理；&lt;/li>
&lt;li>若两者都未处理，则异常会继续冒泡到 &lt;code>CoroutineExceptionHandler&lt;/code>。&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="handled-状态的意义">
 handled 状态的意义&lt;span class="heading__anchor"> &lt;a href="#handled-%e7%8a%b6%e6%80%81%e7%9a%84%e6%84%8f%e4%b9%89">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;code>makeHandled()&lt;/code> 的作用是将 &lt;code>CompletedExceptionally&lt;/code> 标记为已处理（&lt;code>handled = true&lt;/code>）。
这个标志会影响 &lt;code>onCompletionInternal&lt;/code> 的行为：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">protected&lt;/span> &lt;span class="k">final&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onCompletionInternal&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">?)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">CompletedExceptionally&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">onCancelled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">cause&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">handled&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">onCompleted&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>也就是说，当 &lt;code>handled&lt;/code> 为 &lt;code>false&lt;/code> 时，异常会被进一步上报。&lt;/p>
&lt;p>在 &lt;code>SupervisorJob&lt;/code> 中，由于 &lt;code>childCancelled&lt;/code> 返回 &lt;code>false&lt;/code>，
子任务的异常不会让父协程进入取消状态，也不会设置 &lt;code>handled = true&lt;/code>。
结果就是：&lt;strong>异常只会在当前子协程内抛出，不会影响其他子协程。&lt;/strong>&lt;/p>
&lt;h2 class="heading" id="结论">
 结论&lt;span class="heading__anchor"> &lt;a href="#%e7%bb%93%e8%ae%ba">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>SupervisorJob 通过重写 childCancelled，阻止了子协程异常向父协程传播，从而让协程之间相互独立。&lt;/p>
&lt;p>在 Job 中，任何子协程的异常都会触发 cancelImpl → notifyCancelling → cancelParent 链条，导致整个作用域被取消。
而在 SupervisorJob 中，这条链被截断，父协程不再传播取消信号，异常只在本地处理。&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>对比项&lt;/th>
 &lt;th>&lt;code>Job&lt;/code>&lt;/th>
 &lt;th>&lt;code>SupervisorJob&lt;/code>&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>子任务异常&lt;/td>
 &lt;td>会取消父任务&lt;/td>
 &lt;td>不会取消父任务&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>兄弟任务影响&lt;/td>
 &lt;td>相互影响（一个失败全部取消）&lt;/td>
 &lt;td>相互独立（失败互不影响）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>childCancelled&lt;/td>
 &lt;td>触发父级取消逻辑&lt;/td>
 &lt;td>返回 false，忽略子取消&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>handled 状态&lt;/td>
 &lt;td>置为 true（异常被处理）&lt;/td>
 &lt;td>仍为 false（异常局部处理）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>所以，当你希望在一个作用域中，某个子任务失败时&lt;strong>不影响其他任务&lt;/strong>，
&lt;code>SupervisorJob&lt;/code> 就是你要用的那个。&lt;/p></description></item><item><title>Paging3核心解析：Kotlin Flow 如何实现内存与数据双高效</title><link>https://opoojkk.com/posts/2025/10/07/paging3-core-analysis-kotlin-flow-efficiency/</link><pubDate>Tue, 07 Oct 2025 14:25:55 +0800</pubDate><guid>https://opoojkk.com/posts/2025/10/07/paging3-core-analysis-kotlin-flow-efficiency/</guid><description>&lt;h2 class="heading" id="paging-库是什么">
 Paging 库是什么？&lt;span class="heading__anchor"> &lt;a href="#paging-%e5%ba%93%e6%98%af%e4%bb%80%e4%b9%88">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Paging 库的核心作用是帮助 Android 应用高效地加载和展示大数据集。&lt;/p>
&lt;p>人话解释： 它就像一个聪明的服务员，不会一次性把所有菜都端上来（避免内存爆炸），而是根据你的需求（分页）逐步、按需从本地数据库或网络获取一小部分数据。这样能显著降低内存压力，提供流畅的用户体验。&lt;/p>
&lt;h2 class="heading" id="paging-库如何使用">
 Paging 库如何使用？&lt;span class="heading__anchor"> &lt;a href="#paging-%e5%ba%93%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在 Jetpack Compose 或使用 Kotlin Flow 的架构中，Paging 库的标准用法是创建一个 &lt;code>Flow&amp;lt;PagingData&amp;lt;T&amp;gt;&amp;gt;&lt;/code> 来暴露给 UI 层。&lt;/p>
&lt;h3 class="heading" id="核心代码示例">
 核心代码示例&lt;span class="heading__anchor"> &lt;a href="#%e6%a0%b8%e5%bf%83%e4%bb%a3%e7%a0%81%e7%a4%ba%e4%be%8b">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="p">/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">*&lt;/span> &lt;span class="n">UI&lt;/span> &lt;span class="n">层的不可变状态流&lt;/span>&lt;span class="err">。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">*/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">items&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Flow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">PagingData&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Article&lt;/span>&lt;span class="p">&amp;gt;&amp;gt;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">Pager&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 配置分页行为
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">config&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">PagingConfig&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pageSize&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">ITEMS_PER_PAGE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 每页数据量，唯一必传参数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">enablePlaceholders&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 数据源工厂，每次都创建新的 PagingSource 实例
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">pagingSourceFactory&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">repository&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">articlePagingSource&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">}&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">flow&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 关键优化：缓存 Paging 流程，即使 UI (Activity/Fragment) 经历生命周期变化，
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 分页状态和数据依然保留在 ViewModel 作用域内，无需从头开始加载。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">.&lt;/span>&lt;span class="n">cachedIn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">viewModelScope&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="原理解析pager-的壳与-flow-的构建">
 原理解析：Pager 的“壳”与 Flow 的构建&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e7%90%86%e8%a7%a3%e6%9e%90pager-%e7%9a%84%e5%a3%b3%e4%b8%8e-flow-%e7%9a%84%e6%9e%84%e5%bb%ba">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ol>
&lt;li>&lt;code>Pager&lt;/code> 对象：它是一个“壳子”，封装了 &lt;code>Flow&lt;/code> 的创建过程。&lt;/li>
&lt;li>&lt;code>PagingConfig&lt;/code>：决定分页配置，&lt;code>pageSize&lt;/code> 是唯一必须的参数。&lt;/li>
&lt;li>&lt;code>pagingSourceFactory&lt;/code>：
&lt;ul>
&lt;li>为什么是 Factory（工厂模式）？ 借助 Kotlin 高阶函数的灵活性，它能确保每次都需要创建新的 &lt;code>PagingSource&lt;/code> 实例。&lt;/li>
&lt;li>目的： 保证每次加载时，内部的状态和参数都是最新的，避免使用旧的状态。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>内部实现：真正的分页逻辑藏在更深层。它通过 &lt;code>StateFlow&lt;/code> 来维护状态，并经过一系列配置和操作：
&lt;ul>
&lt;li>刷新触发：当 &lt;code>flow&lt;/code> 开始收集时，会触发首次刷新加载。&lt;/li>
&lt;li>无缝衔接：通过对加载位置的变换，新加载的数据能够从用户当前查看的位置附近开始，实现无缝的数据替换。&lt;/li>
&lt;li>状态合并：利用 &lt;code>combineWithoutBatching&lt;/code> 等 Flow 操作符，将请求状态事件和请求结果进行结合，最终发送出 &lt;code>PagingData&lt;/code> 结果。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="paging-库的核心组件">
 Paging 库的核心组件&lt;span class="heading__anchor"> &lt;a href="#paging-%e5%ba%93%e7%9a%84%e6%a0%b8%e5%bf%83%e7%bb%84%e4%bb%b6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Paging 库主要由三个核心部分组成：&lt;/p>
&lt;h3 class="heading" id="pager-与-flow">
 Pager 与 Flow&lt;span class="heading__anchor"> &lt;a href="#pager-%e4%b8%8e-flow">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>如上所述，负责配置和启动分页数据流。&lt;/p>
&lt;h3 class="heading" id="pagingsource">
 PagingSource&lt;span class="heading__anchor"> &lt;a href="#pagingsource">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>这是您实现数据获取逻辑的地方，作用相对简单但至关重要：&lt;/p>
&lt;ol>
&lt;li>加载实现：定义如何从网络或数据库加载数据的逻辑（&lt;code>load()&lt;/code> 方法）。&lt;/li>
&lt;li>刷新键（Refresh Key）：定义在刷新操作（例如数据失效或旋转屏幕）时，如何定位到用户正在查看的新起始加载位置。&lt;/li>
&lt;/ol>
&lt;h3 class="heading" id="pagingdataadapter">
 PagingDataAdapter&lt;span class="heading__anchor"> &lt;a href="#pagingdataadapter">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>&lt;code>PagingDataAdapter&lt;/code> 继承自 &lt;code>RecyclerView.Adapter&lt;/code>，它将获取 Item 的工作委托给了内部的 &lt;code>AsyncPagingDataDiffer&lt;/code>，并巧妙地隐藏了后续分页的关键触发逻辑。&lt;/p>
&lt;h2 class="heading" id="关键原理后续分页如何触发">
 关键原理：后续分页如何触发&lt;span class="heading__anchor"> &lt;a href="#%e5%85%b3%e9%94%ae%e5%8e%9f%e7%90%86%e5%90%8e%e7%bb%ad%e5%88%86%e9%a1%b5%e5%a6%82%e4%bd%95%e8%a7%a6%e5%8f%91">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>第一次加载由 Flow 启动，但之后的加载（当用户滚动接近底部时）并不是手动调用的，它被隐藏在了 &lt;code>PagingDataAdapter&lt;/code> 的内部：&lt;/p>
&lt;h3 class="heading" id="核心触发点getitemposition">
 核心触发点：&lt;code>getItem(position)&lt;/code>&lt;span class="heading__anchor"> &lt;a href="#%e6%a0%b8%e5%bf%83%e8%a7%a6%e5%8f%91%e7%82%b9getitemposition">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>当 &lt;code>RecyclerView&lt;/code> 准备展示一个 Item 时，会调用 &lt;code>PagingDataAdapter&lt;/code> 的 &lt;code>getItem(position)&lt;/code> 方法。&lt;/p>
&lt;p>&lt;code>PagingDataAdapter.kt&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// PagingDataAdapter.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">*&lt;/span> &lt;span class="nd">@return&lt;/span> &lt;span class="n">The&lt;/span> &lt;span class="n">presented&lt;/span> &lt;span class="n">item&lt;/span> &lt;span class="n">at&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">position&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">`null`&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="k">it&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="n">placeholder&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">*/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@MainThread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">protected&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">getItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@IntRange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">from&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">position&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">differ&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">position&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="differgetitem-的最终流向">
 Differ：&lt;code>getItem&lt;/code> 的最终流向&lt;span class="heading__anchor"> &lt;a href="#differgetitem-%e7%9a%84%e6%9c%80%e7%bb%88%e6%b5%81%e5%90%91">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ol>
&lt;li>&lt;code>getItem&lt;/code> 调用会进入内部的 &lt;code>AsyncPagingDataDiffer&lt;/code>，并最终执行到 &lt;code>PagingDataPresenter&lt;/code> 的 &lt;code>get(index)&lt;/code>。&lt;/li>
&lt;li>在 &lt;code>getItem&lt;/code> 方法内部，会更新上一次访问的索引（&lt;code>lastAccessedIndex = index&lt;/code>）。&lt;/li>
&lt;/ol>
&lt;p>&lt;code>AsyncPagingDataDiffer.kt&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@MainThread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fun&lt;/span> &lt;span class="nf">getItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@IntRange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">from&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">index&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... 省略无关代码
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">lastAccessedIndex&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">index&lt;/span> &lt;span class="c1">// 记录当前访问的位置
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">presenter&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">finally&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="viewporthint将-ui-行为转化为加载事件">
 ViewportHint：将 UI 行为转化为加载事件&lt;span class="heading__anchor"> &lt;a href="#viewporthint%e5%b0%86-ui-%e8%a1%8c%e4%b8%ba%e8%bd%ac%e5%8c%96%e4%b8%ba%e5%8a%a0%e8%bd%bd%e4%ba%8b%e4%bb%b6">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>在 &lt;code>PagingDataPresenter&lt;/code> 的 &lt;code>get(index)&lt;/code> 方法中，核心逻辑是根据 &lt;code>index&lt;/code> 创建并发送一个 &lt;code>ViewportHint&lt;/code>。&lt;/p>
&lt;p>调用的顺序和原理：&lt;/p>
&lt;ol>
&lt;li>&lt;code>PagingDataPresenter.get()&lt;/code>&lt;/li>
&lt;li>-&amp;gt;&lt;code>HintReceiver.accessHint()&lt;/code>&lt;/li>
&lt;li>-&amp;gt; &lt;code>PagerHintReceiver.accessHint()&lt;/code>&lt;/li>
&lt;li>-&amp;gt; &lt;code>PageFetcherSnapshot.processHint(viewportHint)&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>&lt;code>processHint&lt;/code> 的作用：&lt;/p>
&lt;p>&lt;code>ViewportHint&lt;/code> 包含了用户当前在 UI 中可见的区域和滚动方向。&lt;code>PageFetcherSnapshot&lt;/code> 接收到这个 Hint 后：&lt;/p>
&lt;ul>
&lt;li>它会更新内部状态，确定是需要预加载下一页（&lt;code>APPEND&lt;/code>）还是预加载上一页（&lt;code>PREPEND&lt;/code>）。&lt;/li>
&lt;li>最终，它会通过 &lt;code>pageEventFlow&lt;/code> 启动两个后台协程。这些协程会不断处理收到的事件，并执行 &lt;code>doLoad&lt;/code> 方法，从而悄无声息地完成了下一页数据的加载。&lt;/li>
&lt;/ul>
&lt;p>结论： Paging 库通过拦截 &lt;code>RecyclerView&lt;/code> 的 &lt;code>getItem&lt;/code> 调用，将其转换为一个 &lt;code>ViewportHint&lt;/code> 事件，并利用内部的 Flow 机制，实现了加载逻辑与 UI 滚动的解耦和自动化触发。&lt;/p></description></item><item><title>深入 Compose：从 setContent 到 LayoutNode 绘制原理</title><link>https://opoojkk.com/posts/2025/10/06/deep-dive-compose-setcontent-to-layoutnode/</link><pubDate>Mon, 06 Oct 2025 22:05:44 +0800</pubDate><guid>https://opoojkk.com/posts/2025/10/06/deep-dive-compose-setcontent-to-layoutnode/</guid><description>&lt;p>在之前，我一直以为 Compose 是深不可测、难以理解的东西，直到我打开一个 Compose 项目的 &lt;code>setContent&lt;/code> 方法，才发现它其实并没有那么复杂。&lt;/p>
&lt;p>Compose 和 XML 的不同，只是写 UI 的方式不同而已。而关键差别，就在 &lt;code>setContent&lt;/code> 方法上。注意，这里的 &lt;code>setContent&lt;/code> 与我们平时在 Activity 中调用的、传入一个 View 或布局 id 的 &lt;code>setContentView&lt;/code> 并不是同一个方法。它只是名字相同，实际上完全是另一套实现：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">ComponentActivity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">setContent&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">parent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CompositionContext&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nd">@Composable&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Unit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">existingComposeView&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">decorView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findViewById&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">getChildAt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="n">ComposeView&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">existingComposeView&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">with&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">existingComposeView&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">setParentCompositionContext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">setContent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ComposeView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">apply&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Set content and parent **before** setContentView
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// to have ComposeView create the composition on attach
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">setParentCompositionContext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">setContent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Set the view tree owners before setting the content view so that the inflation
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// process and attach listeners will see them already present
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">setOwners&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">setContentView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">DefaultActivityContentLayoutParams&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>可以看到，&lt;code>ComposeView&lt;/code> 继承自 &lt;code>AbstractComposeView&lt;/code>，而 &lt;code>AbstractComposeView&lt;/code> 又是一个 &lt;code>ViewGroup&lt;/code>。当 &lt;code>ComposeView&lt;/code> 被添加到 Window 上时，会创建一个 &lt;code>Composition&lt;/code> 对象：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ComposeView.android.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onAttachedToWindow&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">onAttachedToWindow&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">previousAttachedWindowToken&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">windowToken&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">shouldCreateCompositionOnAttachedToWindow&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ensureCompositionCreated&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">ensureCompositionCreated&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">composition&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">creatingComposition&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">composition&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">setContent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resolveParentCompositionContext&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Content&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">finally&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">creatingComposition&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>ComposeView&lt;/code> 的 &lt;code>setContent&lt;/code> 会创建一个关键的类：&lt;code>AndroidComposeView&lt;/code>。可以说它是 Compose 在 Android 上的核心实现：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Wrapper.android.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">internal&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">AbstractComposeView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">setContent&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">parent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CompositionContext&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nd">@Composable&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Unit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">):&lt;/span> &lt;span class="n">Composition&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">GlobalSnapshotManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ensureStarted&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">composeView&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">childCount&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">getChildAt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="n">AndroidComposeView&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">removeAllViews&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="o">?:&lt;/span> &lt;span class="n">AndroidComposeView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">parent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">effectCoroutineContext&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">also&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">addView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">DefaultLayoutParams&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">doSetContent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">composeView&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">parent&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>AndroidComposeView&lt;/code> 是 &lt;code>ComposeView&lt;/code> 的唯一子 View，它把 Compose 和 Android View 系统联系起来。&lt;/p>
&lt;p>在 &lt;code>AndroidComposeView&lt;/code> 中，根节点就是 Compose 的 &lt;code>LayoutNode&lt;/code>。Compose 中的每个组件，都会由 &lt;code>@Composable&lt;/code> 标记，并在编译时由 Kotlin IR 插件自动增加两个参数：&lt;code>Composer $composer&lt;/code> 和 &lt;code>int $changed&lt;/code>。&lt;code>Composer&lt;/code> 的唯一实现类 &lt;code>ComposerImpl&lt;/code> 会为组件创建 &lt;code>LayoutNode&lt;/code>，而 &lt;code>Applier&lt;/code> 则负责把 &lt;code>LayoutNode&lt;/code> 添加到父节点中：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Wrapper.android.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">doSetContent&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">owner&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">AndroidComposeView&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">parent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CompositionContext&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nd">@Composable&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Unit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">):&lt;/span> &lt;span class="n">Composition&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">original&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">Composition&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">UiApplier&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">parent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">wrapped&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">owner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getTag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">wrapped_composition_tag&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">as&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="n">WrappedComposition&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?:&lt;/span> &lt;span class="n">WrappedComposition&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">original&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">also&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">owner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">setTag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">wrapped_composition_tag&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">wrapped&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">setContent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">coroutineContext&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="n">parent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">effectCoroutineContext&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">owner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">coroutineContext&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">parent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">effectCoroutineContext&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">wrapped&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里，&lt;code>owner&lt;/code> 是 &lt;code>AndroidComposeView&lt;/code>，&lt;code>owner.root&lt;/code> 是 Compose 的根节点，用于创建 &lt;code>UiApplier&lt;/code>。&lt;/p>
&lt;hr>
&lt;h2 class="heading" id="绘制流程">
 绘制流程&lt;span class="heading__anchor"> &lt;a href="#%e7%bb%98%e5%88%b6%e6%b5%81%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Compose 的绘制逻辑也不同于传统 View。传统 View 通常只重写 &lt;code>onDraw&lt;/code> 来绘制内容，背景和前景由系统在 &lt;code>draw&lt;/code> 中处理。而 Compose 的绘制单元不是 View，而是 &lt;code>LayoutNode&lt;/code>，因此绘制操作在 &lt;code>dispatchDraw&lt;/code> 中进行分发：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">dispatchDraw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">graphics&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Canvas&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!is&lt;/span>&lt;span class="n">AttachedToWindow&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">invalidateLayers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">measureAndLayout&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">Snapshot&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sendApplyNotifications&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">isDrawingContent&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 绘制根节点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">canvasHolder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">drawInto&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">root&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">draw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">dirtyLayers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isNotEmpty&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="n">until&lt;/span> &lt;span class="n">dirtyLayers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">layer&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">dirtyLayers&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">layer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">updateDisplayList&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nc">ViewLayer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">shouldUseDispatchDraw&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">saveCount&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">canvas&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">save&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">canvas&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">clipRect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">0f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">0f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">0f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">0f&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dispatchDraw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">canvas&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">restoreToCount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">saveCount&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dirtyLayers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">clear&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">isDrawingContent&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">postponedDirtyLayers&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">postponed&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">postponedDirtyLayers&lt;/span>&lt;span class="o">!!&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dirtyLayers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">addAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">postponed&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">postponed&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">clear&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在这里，&lt;code>root&lt;/code> 就是 &lt;code>LayoutNode&lt;/code> 的根节点。绘制时，会遍历链表中的节点，并调用节点自身的 &lt;code>draw&lt;/code> 方法：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// NodeCoordinator.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">fun&lt;/span> &lt;span class="nf">draw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Canvas&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">graphicsLayer&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">GraphicsLayer&lt;/span>&lt;span class="p">?)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">layer&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">layer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">layer&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">layer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">drawLayer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">graphicsLayer&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">x&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">position&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">toFloat&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">y&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">position&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">toFloat&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">canvas&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">translate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">drawContainedDrawModifiers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">graphicsLayer&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">canvas&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">translate&lt;/span>&lt;span class="p">(-&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">drawContainedDrawModifiers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Canvas&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">graphicsLayer&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">GraphicsLayer&lt;/span>&lt;span class="p">?)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">head&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">head&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">Nodes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Draw&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">head&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">performDraw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">graphicsLayer&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">drawScope&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">layoutNode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">mDrawScope&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">drawScope&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">draw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">toSize&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">head&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">graphicsLayer&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// LayoutNodeDrawScope.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">internal&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">draw&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">canvas&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Canvas&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Size&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">coordinator&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">NodeCoordinator&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">drawNode&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nc">Modifier&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">layer&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">GraphicsLayer&lt;/span>&lt;span class="p">?&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">drawNode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dispatchForKind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">Nodes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Draw&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">drawDirect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">coordinator&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">it&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">layer&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>简单总结：&lt;/p>
&lt;ul>
&lt;li>&lt;code>ComposeView&lt;/code> → &lt;code>AndroidComposeView&lt;/code> → &lt;code>LayoutNode&lt;/code>&lt;/li>
&lt;li>&lt;code>@Composable&lt;/code> 函数组件 → &lt;code>ComposerImpl&lt;/code> → &lt;code>LayoutNode&lt;/code> → &lt;code>UiApplier&lt;/code>&lt;/li>
&lt;li>绘制由 &lt;code>dispatchDraw&lt;/code> 触发，通过 &lt;code>Canvas&lt;/code> 绘制 &lt;code>LayoutNode&lt;/code> 的内容&lt;/li>
&lt;li>Compose 的 UI 结构和 View 层级不同，它直接操作 &lt;code>LayoutNode&lt;/code> 树，而不是 View 树&lt;/li>
&lt;/ul></description></item><item><title>Jetpack DataStore：Android应用中的现代数据存储方法</title><link>https://opoojkk.com/posts/2025/09/30/jetpack-datastore-modern-android-storage/</link><pubDate>Tue, 30 Sep 2025 23:13:28 +0800</pubDate><guid>https://opoojkk.com/posts/2025/09/30/jetpack-datastore-modern-android-storage/</guid><description>&lt;p>在学习 DataStore 之前，需要先了解 &lt;strong>Kotlin 属性委托&lt;/strong> 的概念。&lt;/p>
&lt;h2 class="heading" id="kotlin-属性委托简介">
 Kotlin 属性委托简介&lt;span class="heading__anchor"> &lt;a href="#kotlin-%e5%b1%9e%e6%80%a7%e5%a7%94%e6%89%98%e7%ae%80%e4%bb%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Kotlin 支持通过 &lt;strong>属性委托&lt;/strong> (&lt;code>by&lt;/code>) 来委托属性的获取和设置逻辑。&lt;/p>
&lt;p>基本规则是：如果一个对象实现了如下方法：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">operator&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">getValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">thisRef&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">?,&lt;/span> &lt;span class="k">property&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">KProperty&lt;/span>&lt;span class="p">&amp;lt;*&amp;gt;):&lt;/span> &lt;span class="n">A&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>那么就可以用 &lt;code>by 实例&lt;/code> 的方式将属性委托给该对象。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">Context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dataStore&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">DataStore&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Preferences&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="k">by&lt;/span> &lt;span class="n">preferencesDataStore&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;settings&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>上面的写法将 DataStore 实例绑定到 &lt;code>Context&lt;/code> 的扩展属性 &lt;code>dataStore&lt;/code>，访问时会通过委托自动初始化。&lt;/p>
&lt;h2 class="heading" id="创建-datastore-实例">
 创建 DataStore 实例&lt;span class="heading__anchor"> &lt;a href="#%e5%88%9b%e5%bb%ba-datastore-%e5%ae%9e%e4%be%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">EXAMPLE&lt;/span>&lt;span class="n">_COUNTER&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">intPreferencesKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;example_counter&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">exampleCounterFlow&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Flow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Int&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dataStore&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">map&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">preferences&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">preferences&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">EXAMPLE_COUNTER&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">?:&lt;/span> &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>preferencesDataStore&lt;/code> 方法利用 Kotlin 的属性委托创建 DataStore，并通过 &lt;strong>double-check 机制&lt;/strong> 确保整个应用中访问到的都是同一个实例。&lt;/p>
&lt;p>DataStore 是一个数据接口，它主要有两种实现：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Preference DataStore&lt;/strong>：对应 SharedPreferences 风格，使用键值对存储。&lt;/li>
&lt;li>&lt;strong>Proto DataStore&lt;/strong>：对应 ProtoBuf 协议，存储自定义类型，需要手动实现序列化和反序列化。&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="datastore-内部关键组件">
 DataStore 内部关键组件&lt;span class="heading__anchor"> &lt;a href="#datastore-%e5%86%85%e9%83%a8%e5%85%b3%e9%94%ae%e7%bb%84%e4%bb%b6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;code>DataStoreImpl&lt;/code> 是 DataStore 的核心实现类，其中关键变量有：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;code>inMemoryCache: DataStoreInMemoryCache&amp;lt;T&amp;gt;&lt;/code>&lt;/p>
&lt;ul>
&lt;li>存储状态数据，Flow 中的 T 是 &lt;code>State&amp;lt;T&amp;gt;&lt;/code>，可直接拿到当前状态。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;code>readAndInit: InitDataStore()&lt;/code>&lt;/p>
&lt;ul>
&lt;li>负责初始化和读取文件，将 &lt;code>UnInitialized&lt;/code> 状态更新为 &lt;code>Data&lt;/code> 状态。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;code>coordinator: SingleProcessCoordinator&lt;/code>&lt;/p>
&lt;ul>
&lt;li>单进程场景下用于版本控制和锁管理，保证数据一致性。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 class="heading" id="datastore-状态">
 DataStore 状态&lt;span class="heading__anchor"> &lt;a href="#datastore-%e7%8a%b6%e6%80%81">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>DataStore 内部状态包括：&lt;/p>
&lt;ul>
&lt;li>&lt;code>UnInitialized&lt;/code>：初始状态，需要解析文件。&lt;/li>
&lt;li>&lt;code>Data&lt;/code>：数据已初始化，可正常读取。&lt;/li>
&lt;li>&lt;code>ReadException&lt;/code>：读取失败。&lt;/li>
&lt;li>&lt;code>Final&lt;/code>：最终状态，缓存数据不再更新。&lt;/li>
&lt;/ul>
&lt;p>第一次访问 DataStore 时，状态为 &lt;code>UnInitialized&lt;/code>，初始化完成后才会切换到 &lt;code>Data&lt;/code> 状态。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">suspend&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">readState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">requireLock&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Boolean&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">State&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">withContext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">scope&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">coroutineContext&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">inMemoryCache&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">currentState&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">Final&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inMemoryCache&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">currentState&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">readAndInitOrPropagateAndThrowFailure&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">throwable&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Throwable&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="nd">@withContext&lt;/span> &lt;span class="n">ReadException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">throwable&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">readDataAndUpdateCache&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">requireLock&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>readAndInit&lt;/code> 的核心逻辑是 &lt;strong>只执行一次&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">internal&lt;/span> &lt;span class="k">abstract&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">RunOnce&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">runMutex&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">Mutex&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">didRun&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">CompletableDeferred&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Unit&lt;/span>&lt;span class="p">&amp;gt;()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">protected&lt;/span> &lt;span class="k">abstract&lt;/span> &lt;span class="k">suspend&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">doRun&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">suspend&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">runIfNeeded&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">didRun&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isCompleted&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">runMutex&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">withLock&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">didRun&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isCompleted&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">doRun&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">didRun&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">complete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Unit&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">suspend&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">awaitComplete&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">didRun&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">await&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>通过 &lt;code>didRun.isCompleted&lt;/code> 判断是否已经初始化，保证首次初始化只执行一次。&lt;/p>
&lt;h2 class="heading" id="读取数据流程">
 读取数据流程&lt;span class="heading__anchor"> &lt;a href="#%e8%af%bb%e5%8f%96%e6%95%b0%e6%8d%ae%e6%b5%81%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>首次读取会调用 &lt;code>readAndInit.runIfNeeded()&lt;/code> 解析文件。&lt;/li>
&lt;li>初始化完成后，后续读取会直接从 &lt;code>inMemoryCache&lt;/code> 获取数据。&lt;/li>
&lt;li>读取操作是异步的，结合 Flow 可以实现响应式数据更新。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">exampleCounterFlow&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Flow&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Int&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dataStore&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">map&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">preferences&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">preferences&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">EXAMPLE_COUNTER&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">?:&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="写入数据流程">
 写入数据流程&lt;span class="heading__anchor"> &lt;a href="#%e5%86%99%e5%85%a5%e6%95%b0%e6%8d%ae%e6%b5%81%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>写入流程与 SharedPreferences 类似，但更加安全和异步：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">lifecycleScope&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">launch&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataStore&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">edit&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">settings&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">currentCounterValue&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">settings&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">EXAMPLE_COUNTER&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">?:&lt;/span> &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">settings&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">EXAMPLE_COUNTER&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">currentCounterValue&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>DataStore 写入特点：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>数据先更新到内存缓存。&lt;/li>
&lt;li>再写入临时文件（.tmp）。&lt;/li>
&lt;li>临时文件写入成功后，覆盖原文件。&lt;/li>
&lt;li>通过版本号和锁机制保证多线程/多进程访问一致性。&lt;/li>
&lt;/ol>
&lt;p>示例核心实现：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">internal&lt;/span> &lt;span class="k">suspend&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">writeData&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">newData&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">updateCache&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Boolean&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">Int&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">var&lt;/span> &lt;span class="py">newVersion&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">storageConnection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">writeScope&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">newVersion&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">coordinator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">incrementAndGetVersion&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">writeData&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">newData&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">updateCache&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inMemoryCache&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">tryUpdate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">newData&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">newData&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">hashCode&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">newVersion&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">newVersion&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="datastore-与-sharedpreferences-对比">
 DataStore 与 SharedPreferences 对比&lt;span class="heading__anchor"> &lt;a href="#datastore-%e4%b8%8e-sharedpreferences-%e5%af%b9%e6%af%94">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>特性&lt;/th>
 &lt;th>SharedPreferences&lt;/th>
 &lt;th>DataStore&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>存储方式&lt;/td>
 &lt;td>XML 文件&lt;/td>
 &lt;td>Proto / Preferences Map&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>初始化&lt;/td>
 &lt;td>同步解析 XML，可能导致 ANR&lt;/td>
 &lt;td>异步解析，基于 Flow&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>写入&lt;/td>
 &lt;td>onPause/onStop 阻塞磁盘写入&lt;/td>
 &lt;td>先写 tmp 文件，再覆盖，异步安全&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>多线程/多进程&lt;/td>
 &lt;td>支持有限，多线程容易出错&lt;/td>
 &lt;td>通过锁和版本号保证一致性&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>类型安全&lt;/td>
 &lt;td>仅基本类型&lt;/td>
 &lt;td>Preferences 无类型安全，Proto 可自定义类型&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;h2 class="heading" id="proto-datastore">
 Proto DataStore&lt;span class="heading__anchor"> &lt;a href="#proto-datastore">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Proto DataStore 使用 Protobuf 存储数据：&lt;/p>
&lt;ul>
&lt;li>类型由外部定义，DataStore 不解析具体格式。&lt;/li>
&lt;li>需要实现序列化和反序列化逻辑。&lt;/li>
&lt;li>上层使用方式与 Preference DataStore 类似，但底层存储格式为 Proto。&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="多进程支持">
 多进程支持&lt;span class="heading__anchor"> &lt;a href="#%e5%a4%9a%e8%bf%9b%e7%a8%8b%e6%94%af%e6%8c%81">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>DataStore 支持多进程访问，区别在于锁机制：&lt;/p>
&lt;ul>
&lt;li>单进程使用 &lt;code>Mutex&lt;/code>。&lt;/li>
&lt;li>多进程使用 &lt;code>fcntl&lt;/code> 文件锁，对整个文件加锁，保证跨进程数据一致性。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">lockFileStream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getChannel&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">tryLock&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* position= */&lt;/span> &lt;span class="m">0L&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* size= */&lt;/span> &lt;span class="nc">Long&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">MAX_VALUE&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* shared= */&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>DataStore 相比 SharedPreferences 的优势：&lt;/p>
&lt;ol>
&lt;li>异步、安全，避免 ANR。&lt;/li>
&lt;li>先写入临时文件，再覆盖原文件，保证数据一致性。&lt;/li>
&lt;li>支持 Flow，实现响应式数据更新。&lt;/li>
&lt;li>通过版本号和锁机制，支持多线程与多进程安全。&lt;/li>
&lt;li>支持 Proto 格式，类型安全可扩展。&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="参考资料">
 参考资料&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://kotlinlang.org/docs/delegated-properties.html">Kotlin 属性委托&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/338725227">Linux 文件锁实现之 flock 与 fcntl&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Protocol Buffers编码原理</title><link>https://opoojkk.com/posts/2025/09/24/proto-protocal/</link><pubDate>Wed, 24 Sep 2025 22:08:47 +0800</pubDate><guid>https://opoojkk.com/posts/2025/09/24/proto-protocal/</guid><description>&lt;p>Protocol Buffers（简称 &lt;strong>proto&lt;/strong>）是一种高效的二进制序列化协议。它的核心编码方式基于 &lt;strong>Base-128 Varint（128 进制变长整数）&lt;/strong>。理解它的编码规则是掌握 proto 的关键。&lt;/p>
&lt;h2 class="heading" id="基于-base-128-的-varint-编码">
 基于 Base-128 的 Varint 编码&lt;span class="heading__anchor"> &lt;a href="#%e5%9f%ba%e4%ba%8e-base-128-%e7%9a%84-varint-%e7%bc%96%e7%a0%81">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;strong>Varint&lt;/strong> 是一种可变长度整数的编码方式。一个整数可以用 1~10 个字节存储，数值越小，占用的字节越少。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>每个字节有 8 位，其中：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>最高位&lt;/strong>（MSB，Most Significant Bit）是标志位，用来表示&lt;strong>是否还有后续字节&lt;/strong>。&lt;/p>
&lt;ul>
&lt;li>&lt;code>0&lt;/code> 表示这是最后一个字节。&lt;/li>
&lt;li>&lt;code>1&lt;/code> 表示后面还有字节。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>剩下的 7 位&lt;/strong>存储实际数值。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>举例：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="m">0000&lt;/span> &lt;span class="m">0001&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">^&lt;/span> &lt;span class="n">MSB&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>数字 &lt;code>1&lt;/code> 的 MSB 是 &lt;code>0&lt;/code>，表示这是最后一个字节。&lt;/p>
&lt;p>再看一个稍大的数字 &lt;code>150&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="m">10010110&lt;/span> &lt;span class="m">00000001&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">^&lt;/span> &lt;span class="n">MSB&lt;/span> &lt;span class="p">^&lt;/span> &lt;span class="n">MSB&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="官方计算示例">
 官方计算示例&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%98%e6%96%b9%e8%ae%a1%e7%ae%97%e7%a4%ba%e4%be%8b">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="m">10010110&lt;/span> &lt;span class="m">00000001&lt;/span> &lt;span class="c1">// 原始输入
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="m">0010110&lt;/span> &lt;span class="m">0000001&lt;/span> &lt;span class="c1">// 去掉最高位（MSB）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="m">0000001&lt;/span> &lt;span class="m">0010110&lt;/span> &lt;span class="c1">// 按大端序排列
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="m">00000010010110&lt;/span> &lt;span class="c1">// 拼接
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="m">128&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="m">16&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="m">4&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="m">2&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">150&lt;/span> &lt;span class="c1">// 解释为无符号整数
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>可以看到，&lt;strong>MSB 只用于标识，不参与数值计算&lt;/strong>，最终拼接得到的二进制 &lt;code>00000010010110&lt;/code> 就是 150。&lt;/p>
&lt;h2 class="heading" id="消息结构">
 消息结构&lt;span class="heading__anchor"> &lt;a href="#%e6%b6%88%e6%81%af%e7%bb%93%e6%9e%84">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Protocol Buffers 的二进制消息是一系列&lt;strong>键值对&lt;/strong>。编码时并不存储字段名称，而是使用&lt;strong>字段编号&lt;/strong>（field number）作为键，类型则由一个叫 &lt;strong>wire type&lt;/strong> 的值表示。&lt;/p>
&lt;h3 class="heading" id="wire-type">
 wire type&lt;span class="heading__anchor"> &lt;a href="#wire-type">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>可以理解为“底层存储格式”，不同类型的字段对应不同的 wire type。&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>ID&lt;/th>
 &lt;th>Name&lt;/th>
 &lt;th>说明 / 对应类型&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>0&lt;/td>
 &lt;td>VARINT&lt;/td>
 &lt;td>int32, int64, uint32, uint64, sint32, sint64, bool, enum&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>1&lt;/td>
 &lt;td>I64&lt;/td>
 &lt;td>fixed64, sfixed64, double&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>2&lt;/td>
 &lt;td>LEN&lt;/td>
 &lt;td>string, bytes, 嵌套 message, packed repeated&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>3&lt;/td>
 &lt;td>SGROUP&lt;/td>
 &lt;td>group start（已弃用）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>4&lt;/td>
 &lt;td>EGROUP&lt;/td>
 &lt;td>group end（已弃用）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>5&lt;/td>
 &lt;td>I32&lt;/td>
 &lt;td>fixed32, sfixed32, float&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>&lt;strong>字段头（tag）&lt;/strong> 也是一个 Varint，由以下两部分组成：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>高位部分&lt;/strong>：字段编号（field number）左移 3 位&lt;/li>
&lt;li>&lt;strong>低 3 位&lt;/strong>：wire type&lt;/li>
&lt;/ul>
&lt;p>例如：
一个字节的结构可以粗略理解为：&lt;/p>
&lt;ul>
&lt;li>第 1 位：标识 Varint 是否还有后续字节（若是 Varint 类型）&lt;/li>
&lt;li>接下来的几位：字段编号&lt;/li>
&lt;li>最后 3 位：wire type&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="整数编码">
 整数编码&lt;span class="heading__anchor"> &lt;a href="#%e6%95%b4%e6%95%b0%e7%bc%96%e7%a0%81">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="bool-和-enum">
 bool 和 enum&lt;span class="heading__anchor"> &lt;a href="#bool-%e5%92%8c-enum">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ul>
&lt;li>&lt;code>bool&lt;/code> 与 &lt;code>enum&lt;/code> 都使用 &lt;code>int32&lt;/code> 编码。&lt;/li>
&lt;li>&lt;code>bool&lt;/code> 的值编码为 &lt;code>0&lt;/code>（false）或 &lt;code>1&lt;/code>（true）。&lt;/li>
&lt;/ul>
&lt;h3 class="heading" id="有符号整数">
 有符号整数&lt;span class="heading__anchor"> &lt;a href="#%e6%9c%89%e7%ac%a6%e5%8f%b7%e6%95%b4%e6%95%b0">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>&lt;strong>Varint&lt;/strong> 本身只能表示&lt;strong>无符号整数&lt;/strong>，如果直接把负数当成普通整数来编码，就会被视为一个很大的无符号数，占用的字节数也会增多。
为了让负数也能紧凑地表示，Proto 在编码前会使用 &lt;strong>ZigZag&lt;/strong> 进行转换，把有符号整数映射到无符号整数空间。&lt;/p>
&lt;p>为什么需要这样的转换，可以先看计算机中**补码（Two’s Complement）**的表示方式：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="m">5&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="m">8&lt;/span>&lt;span class="n">位&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">00000101&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">补码&lt;/span>&lt;span class="p">(-&lt;/span>&lt;span class="m">5&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">2&lt;/span>&lt;span class="p">^&lt;/span>&lt;span class="m">8&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="p">(-&lt;/span>&lt;span class="m">5&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">256&lt;/span> &lt;span class="p">-&lt;/span> &lt;span class="m">5&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">251&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">二进制&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">11111011&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在 8 位系统中，&lt;code>-5&lt;/code> 的补码是 &lt;code>11111011&lt;/code>。
如果把 &lt;code>5&lt;/code> 和 &lt;code>-5&lt;/code> 相加：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> 00000101 (5)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+ 11111011 (-5)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1_00000000 // 忽略溢出 → 结果为 0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>负数的补码设计，就是为了让它能与对应的正数直接相加得到 0。
但对 Varint 来说，这个“补码”只是一个极大的无符号值（这里是 251），导致编码冗长。&lt;/p>
&lt;p>ZigZag 的作用就是&lt;strong>重新排列整数到无符号空间&lt;/strong>，让靠近 0 的正负数都能获得更短的编码。
映射规律如下：&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>原始值&lt;/th>
 &lt;th>ZigZag 编码&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>0&lt;/td>
 &lt;td>0&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>-1&lt;/td>
 &lt;td>1&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>1&lt;/td>
 &lt;td>2&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>-2&lt;/td>
 &lt;td>3&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>…&lt;/td>
 &lt;td>…&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>计算公式（以 32 位为例）：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">(n &amp;lt;&amp;lt; 1) ^ (n &amp;gt;&amp;gt; 31)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>其中：&lt;/p>
&lt;ul>
&lt;li>&lt;code>n &amp;lt;&amp;lt; 1&lt;/code>：左移一位，为符号腾出空间。&lt;/li>
&lt;li>&lt;code>n &amp;gt;&amp;gt; 31&lt;/code>：提取符号位（正数全 0，负数全 1）。&lt;/li>
&lt;li>异或运算保证正负数在无符号整数中交替分布。&lt;/li>
&lt;/ul>
&lt;p>经过 ZigZag 转换后，再用普通 Varint 编码，就能在保持正负数信息的同时，&lt;strong>保证数值越接近 0，占用的字节越少&lt;/strong>。&lt;/p>
&lt;h2 class="heading" id="固定长度类型">
 固定长度类型&lt;span class="heading__anchor"> &lt;a href="#%e5%9b%ba%e5%ae%9a%e9%95%bf%e5%ba%a6%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>对于 double、fixed64 等需要固定大小的类型，直接占用对应的字节数：&lt;/p>
&lt;ul>
&lt;li>&lt;code>double&lt;/code> / &lt;code>fixed64&lt;/code> 对应 wire type &lt;strong>I64&lt;/strong>，占 8 字节。&lt;/li>
&lt;li>&lt;code>fixed32&lt;/code> / &lt;code>float&lt;/code> 对应 wire type &lt;strong>I32&lt;/strong>，占 4 字节。&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="长度限定length-delimited">
 长度限定（Length-delimited）&lt;span class="heading__anchor"> &lt;a href="#%e9%95%bf%e5%ba%a6%e9%99%90%e5%ae%9alength-delimited">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;strong>string&lt;/strong>、&lt;strong>bytes&lt;/strong>、嵌套 message、packed repeated 等类型，使用 wire type &lt;strong>LEN&lt;/strong> 编码。
其结构是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[字段头] [长度] [数据字节...]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>其中 &lt;strong>长度&lt;/strong> 也是 Varint 编码。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-proto" data-lang="proto">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">message&lt;/span> &lt;span class="nc">Test2&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当 &lt;code>b&lt;/code> 的值是 &lt;code>&amp;quot;testing&amp;quot;&lt;/code> 时，编码结果为：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">12 07 74 65 73 74 69 6e 67
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>逐字节解释：&lt;/p>
&lt;ul>
&lt;li>&lt;code>12&lt;/code>：字段编号 2（binary 0010）和 wire type 2 组合&lt;/li>
&lt;li>&lt;code>07&lt;/code>：字符串长度 7&lt;/li>
&lt;li>&lt;code>74 65 73 74 69 6e 67&lt;/code>：ASCII 字符串 &amp;ldquo;testing&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="嵌套-message">
 嵌套 Message&lt;span class="heading__anchor"> &lt;a href="#%e5%b5%8c%e5%a5%97-message">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>子消息字段同样使用 &lt;strong>LEN&lt;/strong> 编码。
例如：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-proto" data-lang="proto">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">message&lt;/span> &lt;span class="nc">Test3&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="n">Test1&lt;/span> &lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>若 &lt;code>Test1.a = 150&lt;/code>，编码结果为：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">1a 03 08 96 01
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>解释：&lt;/p>
&lt;ul>
&lt;li>&lt;code>1a&lt;/code>：字段编号 3，wire type 2&lt;/li>
&lt;li>&lt;code>03&lt;/code>：长度 3&lt;/li>
&lt;li>&lt;code>08 96 01&lt;/code>：子 message 的编码内容（字段 1 的值为 150）&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="listrepeated-字段">
 List（repeated 字段）&lt;span class="heading__anchor"> &lt;a href="#listrepeated-%e5%ad%97%e6%ae%b5">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在 &lt;strong>proto&lt;/strong> 中，&lt;code>repeated&lt;/code> 表示一个字段可以重复多次，相当于列表（list）。
数值类型的 &lt;code>repeated&lt;/code> 字段在 &lt;strong>proto3&lt;/strong> 中默认使用 &lt;strong>packed&lt;/strong> 编码，这是一种更节省空间的方式。&lt;/p>
&lt;h3 class="heading" id="packed-编码原理">
 packed 编码原理&lt;span class="heading__anchor"> &lt;a href="#packed-%e7%bc%96%e7%a0%81%e5%8e%9f%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>packed 会把所有元素打包进&lt;strong>一个字段&lt;/strong>，而不是为每个元素重复写入字段头。
编码格式：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[字段头] [总长度] [值1][值2][值3]...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>例如：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-proto" data-lang="proto">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">message&lt;/span> &lt;span class="nc">TestList&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">repeated&lt;/span> &lt;span class="kt">int32&lt;/span> &lt;span class="n">nums&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当 &lt;code>nums = [3, 270]&lt;/code> 时，编码结果是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">0A 03 03 8E 02
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>逐字节解释：&lt;/p>
&lt;ul>
&lt;li>&lt;code>0A&lt;/code>：字段编号 1，wire type 2（Length-delimited）。&lt;/li>
&lt;li>&lt;code>03&lt;/code>：后续数据总长度为 3 个字节。&lt;/li>
&lt;li>&lt;code>03&lt;/code>：第一个数 3 的 Varint 编码。&lt;/li>
&lt;li>&lt;code>8E 02&lt;/code>：第二个数 270 的 Varint 编码。&lt;/li>
&lt;/ul>
&lt;p>这样只写一次字段头，比非 packed 模式（写多次字段头）更紧凑。&lt;/p>
&lt;h2 class="heading" id="map">
 map&lt;span class="heading__anchor"> &lt;a href="#map">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;code>map&lt;/code> 在底层等价于嵌套 message 的 repeated 字段：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-proto" data-lang="proto">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">message&lt;/span> &lt;span class="nc">Test6&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="n">map&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int32&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>实际等价于：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-proto" data-lang="proto">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">message&lt;/span> &lt;span class="nc">Test6&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="kd">message&lt;/span> &lt;span class="nc">g_Entry&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="kt">int32&lt;/span> &lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">repeated&lt;/span> &lt;span class="n">g_Entry&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="proto-消息大小限制">
 Proto 消息大小限制&lt;span class="heading__anchor"> &lt;a href="#proto-%e6%b6%88%e6%81%af%e5%a4%a7%e5%b0%8f%e9%99%90%e5%88%b6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>单个序列化后的 Proto 消息&lt;strong>最大不能超过 2 GiB&lt;/strong>。&lt;/li>
&lt;li>超过此限制，大多数实现会拒绝序列化或解析。&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://protobuf.dev/programming-guides/encoding/">Protocol Buffers Documentation:Encoding&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/compiler/java/generator.cc">Github protocolbuffers/protobuf&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>重新理解Android事件分发机制</title><link>https://opoojkk.com/posts/2025/09/21/rethinking-the-android-event-dispatch-mechanism/</link><pubDate>Sun, 21 Sep 2025 20:23:17 +0800</pubDate><guid>https://opoojkk.com/posts/2025/09/21/rethinking-the-android-event-dispatch-mechanism/</guid><description>&lt;p>在 Android 中，应用程序存在一个&lt;strong>主线程（Main Thread）&lt;/strong>，也被称为&lt;strong>UI 线程&lt;/strong>。所有与界面相关的操作（如刷新 UI、响应点击）都必须在这个线程上完成。其实不仅是 Android，像 JavaScript 这样的前端运行环境也采取了类似的单线程设计。&lt;/p>
&lt;h3 class="heading" id="为什么要用单线程处理-ui">
 为什么要用单线程处理 UI？&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e7%94%a8%e5%8d%95%e7%ba%bf%e7%a8%8b%e5%a4%84%e7%90%86-ui">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>这样做的好处是显而易见的：&lt;/p>
&lt;ul>
&lt;li>避免多个线程同时修改 UI 带来的&lt;strong>竞争问题&lt;/strong>（比如出现界面错乱）。&lt;/li>
&lt;li>减少使用锁（Lock）带来的&lt;strong>性能损耗&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;p>换句话说，UI 层面采用单线程串行化处理，既简化了模型，又提升了性能和稳定性。&lt;/p>
&lt;hr>
&lt;h3 class="heading" id="looper-与事件异步机制">
 Looper 与事件异步机制&lt;span class="heading__anchor"> &lt;a href="#looper-%e4%b8%8e%e4%ba%8b%e4%bb%b6%e5%bc%82%e6%ad%a5%e6%9c%ba%e5%88%b6">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Android 中的 &lt;strong>Looper/MessageQueue&lt;/strong> 机制几乎是 Android 面试必问的内容。简单来说，Looper 通过一个消息循环（Message Loop），让事件变成异步执行。&lt;/p>
&lt;p>其中有一个关键点是 &lt;strong>同步屏障（Sync Barrier）&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>当系统监听到 &lt;strong>Vsync 信号&lt;/strong> 时，会在消息队列中插入一个同步屏障。&lt;/li>
&lt;li>在屏障存在期间，普通消息无法被处理，只有特定的绘制消息能通过。&lt;/li>
&lt;li>这样可以避免耗时任务阻塞绘制，从而减少掉帧（卡顿）。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 class="heading" id="looper-的阻塞与唤醒">
 Looper 的阻塞与唤醒&lt;span class="heading__anchor"> &lt;a href="#looper-%e7%9a%84%e9%98%bb%e5%a1%9e%e4%b8%8e%e5%94%a4%e9%86%92">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>当 Looper 在消息队列中取不到下一个要处理的事件时，它并不会忙等浪费 CPU，而是进入&lt;strong>阻塞状态&lt;/strong>。
这里 Android 使用的是 &lt;strong>epoll&lt;/strong> 机制，而不是更传统的 &lt;strong>select&lt;/strong> 或 &lt;strong>poll&lt;/strong>。&lt;/p>
&lt;hr>
&lt;h3 class="heading" id="selectpoll-与-epoll-的区别">
 select、poll 与 epoll 的区别&lt;span class="heading__anchor"> &lt;a href="#selectpoll-%e4%b8%8e-epoll-%e7%9a%84%e5%8c%ba%e5%88%ab">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>&lt;strong>select/poll&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>通过一次次**遍历（轮询）**文件描述符（fd）集合，判断哪些 fd 可读写。&lt;/li>
&lt;li>每次调用都需要把整个 fd 集合从用户态拷贝到内核态，维护的数据结构复杂度是 &lt;strong>O(n)&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>epoll&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>将要监听的 fd 加入到内核的 &lt;strong>监听列表&lt;/strong>（红黑树结构）。&lt;/li>
&lt;li>内核在事件发生时，直接把 fd 放入 &lt;strong>就绪列表&lt;/strong>（链表结构）。&lt;/li>
&lt;li>应用层调用时，只需要读取就绪列表，而不必遍历所有 fd。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>epoll 的两个核心优化点：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>减少用户态与内核态之间的开销&lt;/strong>：只需传递发生变化的 fd，而不是整个集合。&lt;/li>
&lt;li>&lt;strong>避免轮询遍历&lt;/strong>：内核会主动通知哪些 fd 就绪，应用层只需处理这些就绪事件。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h3 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ul>
&lt;li>Android 的 UI 线程采用单线程模型，保证了 UI 操作的安全与简洁。&lt;/li>
&lt;li>Looper 机制通过消息循环，实现了事件的异步分发，并利用同步屏障确保绘制的流畅性。&lt;/li>
&lt;li>在消息循环等待事件时，Android 采用了 epoll 机制，既避免了性能低下的轮询，又减少了内核与用户态之间的数据传递。&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://doc.embedfire.com/linux/imx6/linux_base/zh/latest/system_programing/socket_io/socket_io.html">深入了解select、poll、epoll之间的区别&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://imageslr.com/2020/02/27/select-poll-epoll.html">操作系统】I/O 多路复用，select / poll / epoll 详解&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.cnblogs.com/aspirant/p/9166944.html">select、poll、epoll之间的区别(搜狗面试)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/@avocadi/what-is-epoll-9bbc74272f7c">What is epoll?&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>WorkManager是怎样工作的</title><link>https://opoojkk.com/posts/2025/09/20/how-does-workmanager-works/</link><pubDate>Sat, 20 Sep 2025 09:23:00 +0800</pubDate><guid>https://opoojkk.com/posts/2025/09/20/how-does-workmanager-works/</guid><description>&lt;p>在 Android 开发中，&lt;strong>WorkManager&lt;/strong> 是官方推荐的后台任务管理框架。它的特点是 &lt;strong>任务可靠执行&lt;/strong>，即便应用退出或设备重启，也能保证任务最终执行。本文尝试从源码和运行机制角度梳理 WorkManager 的执行逻辑。&lt;/p>
&lt;hr>
&lt;h2 class="heading" id="一个最简单的例子">
 一个最简单的例子&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%80%e4%b8%aa%e6%9c%80%e7%ae%80%e5%8d%95%e7%9a%84%e4%be%8b%e5%ad%90">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">UploadWorker&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">appContext&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">workerParams&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">WorkerParameters&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Worker&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">appContext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">workerParams&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">doWork&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">Result&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 任务逻辑
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nc">Result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">success&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">uploadWorkRequest&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">WorkRequest&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">OneTimeWorkRequestBuilder&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">UploadWorker&lt;/span>&lt;span class="p">&amp;gt;().&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nc">WorkManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getInstance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">enqueue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">uploadWorkRequest&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;code>UploadWorker&lt;/code>：自定义任务。&lt;/li>
&lt;li>&lt;code>enqueue()&lt;/code>：将任务加入队列。&lt;/li>
&lt;li>&lt;code>WorkManager&lt;/code> 是单例，内部依赖 &lt;strong>Application 实例&lt;/strong>，并通过 &lt;code>Provider.Configuration&lt;/code> 提供配置（线程池、协程上下文等）。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 class="heading" id="workmanager-的关键点">
 WorkManager 的关键点&lt;span class="heading__anchor"> &lt;a href="#workmanager-%e7%9a%84%e5%85%b3%e9%94%ae%e7%82%b9">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>基于队列思想&lt;/strong>：和常见任务队列类似，支持依赖、状态跟踪。&lt;/li>
&lt;li>&lt;strong>持久化任务信息&lt;/strong>：任务存储在数据库中，确保系统杀进程后还能恢复。&lt;/li>
&lt;li>&lt;strong>多种调度器实现&lt;/strong>：底层通过 &lt;code>JobScheduler&lt;/code>、&lt;code>GCM&lt;/code> 或 &lt;code>AlarmManager&lt;/code> 触发执行。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 class="heading" id="任务入队数据库层面">
 任务入队：数据库层面&lt;span class="heading__anchor"> &lt;a href="#%e4%bb%bb%e5%8a%a1%e5%85%a5%e9%98%9f%e6%95%b0%e6%8d%ae%e5%ba%93%e5%b1%82%e9%9d%a2">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>数据库核心表：&lt;code>WorkSpec&lt;/code>&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>WorkSpec&lt;/strong>：存储一个逻辑工作单元的全部信息（id、约束条件、状态等）。&lt;/p>
&lt;/blockquote>
&lt;p>入队时的逻辑：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>依赖检查&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>确保所有父任务已经入库（通过 id 判断）。&lt;/li>
&lt;li>根据任务名称和策略（&lt;code>APPEND&lt;/code>、&lt;code>KEEP&lt;/code> 等）处理冲突。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>常见策略&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;code>APPEND&lt;/code> / &lt;code>APPEND_OR_REPLACE&lt;/code>&lt;/p>
&lt;ul>
&lt;li>记录已有任务的依赖 id。&lt;/li>
&lt;li>如果依赖被取消或失败，删除相关任务。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;code>KEEP&lt;/code>&lt;/p>
&lt;ul>
&lt;li>已有任务状态为 &lt;code>ENQUEUED&lt;/code> 或 &lt;code>RUNNING&lt;/code> 时，忽略新任务。&lt;/li>
&lt;li>否则删除旧任务，重新入库。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>插入新记录&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>判断前置任务和执行状态，更新状态。&lt;/li>
&lt;li>插入数据库（&lt;code>WorkSpec&lt;/code>、依赖表、标签表、名称表）。&lt;/li>
&lt;li>返回是否需要实际执行（&lt;code>state == ENQUEUED&lt;/code>）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 class="heading" id="任务执行过程">
 任务执行过程&lt;span class="heading__anchor"> &lt;a href="#%e4%bb%bb%e5%8a%a1%e6%89%a7%e8%a1%8c%e8%bf%87%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>当任务需要执行时：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>标记执行状态&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>队列中触发条件（如 ContentUri）满足。&lt;/li>
&lt;li>状态非完成的任务被调度。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>调度器决定执行方式&lt;/strong>
WorkManager 会在初始化时选择一个合适的 &lt;code>Scheduler&lt;/code>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>API 23+&lt;/strong> → &lt;code>SystemJobScheduler&lt;/code> (基于 &lt;code>JobScheduler&lt;/code>)&lt;/li>
&lt;li>&lt;strong>旧设备&lt;/strong> → 优先用 &lt;code>GcmScheduler&lt;/code>（若有 GCM），否则用 &lt;code>SystemAlarmScheduler&lt;/code> (基于 &lt;code>AlarmManager&lt;/code>)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>执行逻辑&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>SystemJobScheduler&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>保存状态，将 &lt;code>WorkSpec&lt;/code> 与系统 &lt;code>JobInfo&lt;/code> 绑定。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>JobInfo&lt;/code> 配置条件：&lt;/p>
&lt;ul>
&lt;li>充电 / 空闲 / 网络约束&lt;/li>
&lt;li>API 29 前至少需要一个条件（默认延迟）&lt;/li>
&lt;li>前台任务标记&lt;/li>
&lt;li>API 24+ 支持 &lt;code>ContentUri Trigger&lt;/code>&lt;/li>
&lt;li>API 26+ 支持低电量 / 低存储约束&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>API 23 的特殊情况：只有当队列里有两个任务时才会触发 → 会补充一个任务保证执行。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>SystemAlarmScheduler&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>直接通过 &lt;code>startService()&lt;/code> 粗暴触发。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 class="heading" id="worker-的创建与绑定">
 Worker 的创建与绑定&lt;span class="heading__anchor"> &lt;a href="#worker-%e7%9a%84%e5%88%9b%e5%bb%ba%e4%b8%8e%e7%bb%91%e5%ae%9a">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>执行时，WorkManager 根据数据库中的 &lt;code>WorkSpec&lt;/code> &lt;strong>动态创建 Worker 实例&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">worker&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">configuration&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">workerFactory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">createWorkerWithDefaultFallback&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">appContext&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">workSpec&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">workerClassName&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">params&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里的关键点：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>没有直接传递 Worker 实例&lt;/strong>，而是通过 &lt;strong>类名反射&lt;/strong> + &lt;code>WorkerFactory&lt;/code> 创建。&lt;/li>
&lt;li>数据库记录了任务的 &lt;code>workerClassName&lt;/code> 和参数，确保任务可以在应用重启后恢复。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 class="heading" id="稳定执行的保障">
 稳定执行的保障&lt;span class="heading__anchor"> &lt;a href="#%e7%a8%b3%e5%ae%9a%e6%89%a7%e8%a1%8c%e7%9a%84%e4%bf%9d%e9%9a%9c">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>WorkManager 的稳定性来源于以下几个方面：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>持久化&lt;/strong>：所有任务与依赖存储在数据库中。&lt;/li>
&lt;li>&lt;strong>多重调度器适配&lt;/strong>：根据 API Level 和设备环境选择合适的调度方式。&lt;/li>
&lt;li>&lt;strong>状态机驱动&lt;/strong>：任务状态（&lt;code>ENQUEUED&lt;/code>、&lt;code>RUNNING&lt;/code>、&lt;code>FAILED&lt;/code>、&lt;code>SUCCEEDED&lt;/code> 等）严格受数据库驱动。&lt;/li>
&lt;li>&lt;strong>WorkerFactory&lt;/strong>：确保任务实例可恢复，避免直接依赖内存对象。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>WorkManager 的核心是 &lt;strong>数据库 + 调度器&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>数据库 → 保证任务信息和状态持久化。&lt;/li>
&lt;li>调度器 → 保证任务在合适条件下触发执行。&lt;/li>
&lt;li>WorkerFactory → 保证任务对象可恢复。&lt;/li>
&lt;/ul>
&lt;p>因此，无论应用进程是否被杀、设备是否重启，任务都能在条件满足时重新调度并最终执行。这也是 WorkManager 相比于直接使用 &lt;code>JobScheduler&lt;/code>、&lt;code>AlarmManager&lt;/code> 的最大优势。&lt;/p>
&lt;hr>
&lt;p>参考：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://developer.android.com/topic/performance/background-optimization?hl=zh-cn#new-jobinfo">ContentUri Trigger 官方文档&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>权力和权利</title><link>https://opoojkk.com/posts/2025/09/19/power-and-rights/</link><pubDate>Fri, 19 Sep 2025 06:47:18 +0800</pubDate><guid>https://opoojkk.com/posts/2025/09/19/power-and-rights/</guid><description>&lt;p>权力对应英文单词power，通过职位等方式获取到，能支配其他人，是有力量的。就比如警察差身份证。&lt;/p>
&lt;p>权利对应right，法律、规定等规则授予的，在规则下能享受到的利益，像是&lt;del>选举权、被选举权、言论、出版、集会、结社、游行、示威&lt;/del>、受教育（受教育总该有了吧）。&lt;/p></description></item><item><title>Android中的View加载是怎样执行的</title><link>https://opoojkk.com/posts/2025/09/14/how-does-android-inflate-views/</link><pubDate>Sun, 14 Sep 2025 18:05:28 +0800</pubDate><guid>https://opoojkk.com/posts/2025/09/14/how-does-android-inflate-views/</guid><description>&lt;p>在 &lt;strong>Android&lt;/strong> 中，&lt;code>Activity&lt;/code> 是四大组件之一，负责将 &lt;strong>视图&lt;/strong>（&lt;code>View&lt;/code>）与 &lt;strong>生命周期&lt;/strong> 关联。当一个 &lt;code>Activity&lt;/code> 创建时，通常会通过 &lt;code>setContentView&lt;/code> 设置内容，其本质是将一个 &lt;code>View&lt;/code> 添加到系统预留的容器中。&lt;/p>
&lt;p>在这个过程中，&lt;strong>xml 文件会被解析成一个树状结构的 View&lt;/strong>。本文主要介绍 &lt;strong>xml 布局是如何转化为 View 的&lt;/strong>。&lt;/p>
&lt;hr>
&lt;h2 class="heading" id="layoutinflater-的核心流程">
 LayoutInflater 的核心流程&lt;span class="heading__anchor"> &lt;a href="#layoutinflater-%e7%9a%84%e6%a0%b8%e5%bf%83%e6%b5%81%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>加载 xml 布局文件时，会用到 &lt;code>LayoutInflater&lt;/code>，解析逻辑就在其中。
&lt;code>inflate&lt;/code> 的关键逻辑如下（简化保留核心部分）：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="n">View&lt;/span> &lt;span class="n">inflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">XmlPullParser&lt;/span> &lt;span class="n">parser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nd">@Nullable&lt;/span> &lt;span class="n">ViewGroup&lt;/span> &lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">boolean&lt;/span> &lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">final&lt;/span> &lt;span class="n">String&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">parser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getName&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nc">TAG_MERGE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">equals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// merge 标签会合并到父布局中（内部不能再嵌套 merge）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">rInflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">inflaterContext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrs&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 创建根节点 View
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">final&lt;/span> &lt;span class="n">View&lt;/span> &lt;span class="n">temp&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">createViewFromTag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">inflaterContext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrs&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">LayoutParams&lt;/span> &lt;span class="n">params&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">root&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">params&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">root&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">generateLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">temp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">setLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 解析子节点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">rInflateChildren&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">temp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrs&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">root&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">null&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">root&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">addView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">temp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">params&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">root&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">null&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">?&lt;/span> &lt;span class="n">temp&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">root&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里可以看到分为两种情况：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>&lt;code>merge&lt;/code> 标签&lt;/strong>：直接展开合并到父布局，内部不能再嵌套 &lt;code>merge&lt;/code>。&lt;/li>
&lt;li>&lt;strong>普通标签&lt;/strong>：通过 &lt;code>createViewFromTag&lt;/code> 创建根节点，然后递归解析子节点。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 class="heading" id="rinflate-的解析子节点逻辑">
 rInflate 的解析子节点逻辑&lt;span class="heading__anchor"> &lt;a href="#rinflate-%e7%9a%84%e8%a7%a3%e6%9e%90%e5%ad%90%e8%8a%82%e7%82%b9%e9%80%bb%e8%be%91">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;code>rInflate&lt;/code> 方法中，会继续递归创建子 View 并添加到父容器：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// LayoutInflater.java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">rInflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">XmlPullParser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AttributeSet&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">finishInflate&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">createViewFromTag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">viewGroup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LayoutParams&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">viewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">generateLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 递归解析子节点&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">rInflateChildren&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 添加到父容器&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">viewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>而 &lt;code>rInflateChildren&lt;/code> 只是对 &lt;code>rInflate&lt;/code> 的再封装：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">rInflateChildren&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">XmlPullParser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AttributeSet&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">finishInflate&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">rInflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getContext&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">finishInflate&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 class="heading" id="view-的创建方式">
 View 的创建方式&lt;span class="heading__anchor"> &lt;a href="#view-%e7%9a%84%e5%88%9b%e5%bb%ba%e6%96%b9%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在创建 View 时，分为两类：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>常见控件（TextView、ImageView、Button 等）&lt;/strong>：通过 &lt;code>AppCompatViewInflater&lt;/code> 的枚举分支直接创建。&lt;/li>
&lt;li>&lt;strong>其他自定义或不在枚举中的控件&lt;/strong>：通过反射调用构造方法创建。&lt;/li>
&lt;/ol>
&lt;p>这也是为什么所有自定义 View 必须要有 &lt;strong>固定签名的构造方法&lt;/strong>（如 &lt;code>(Context context, AttributeSet attrs)&lt;/code>）。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">createViewByPrefix&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">prefix&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Constructor&lt;/span>&lt;span class="o">&amp;lt;?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">constructor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sConstructorMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">constructor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Class&lt;/span>&lt;span class="o">&amp;lt;?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">clazz&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Class&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">forName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">prefix&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prefix&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getClassLoader&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">asSubclass&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">constructor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">clazz&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getConstructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sConstructorSignature&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sConstructorMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">constructor&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">constructor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setAccessible&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">constructor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">newInstance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mConstructorArgs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 class="heading" id="其他细节">
 其他细节&lt;span class="heading__anchor"> &lt;a href="#%e5%85%b6%e4%bb%96%e7%bb%86%e8%8a%82">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>如果 xml 中定义了 &lt;code>android:onClick&lt;/code>，会在这里解析并绑定对应的方法（虽然现在已经很少使用这种写法）。&lt;/li>
&lt;li>布局文件中所有非 &lt;code>merge&lt;/code> 根节点及其子 View，最终都会通过 &lt;code>rInflate&lt;/code> 的递归过程，逐一生成并挂载到父容器。&lt;/li>
&lt;/ul></description></item><item><title>技术文章的价值思考</title><link>https://opoojkk.com/posts/2025/09/09/do-we-still-need-technical-articles/</link><pubDate>Tue, 09 Sep 2025 21:08:34 +0800</pubDate><guid>https://opoojkk.com/posts/2025/09/09/do-we-still-need-technical-articles/</guid><description>&lt;p>前几天我看到一个帖子，讨论的问题是：“在 AI 时代，技术文章还有必要吗？” 这是个有些“老派”的提法，但依然引起了不少人讨论。大多数人的观点是：没什么必要了。因为相比 AI 掌握的庞大知识，技术文章的那点内容微不足道。&lt;/p>
&lt;p>我并不认同这种看法。可以从“读的人”和“写的人”两个角度来理解：&lt;/p>
&lt;p>首先，从读者的角度看。
AI 的确在训练过程中使用了海量资料，再加上 RAG 技术的加持，让它仿佛拥有了超越个人的知识广度。但这些知识从何而来？它们的源头还是人类。网络上的文章、文档、讨论帖子，都是人们写下来的知识积累。如果没有最初愿意输出的那群人，就不会有今天 AI 能够回答的内容。换句话说，我们写下的东西，未来也可能成为 AI 学习的素材，从而帮助更多人。&lt;/p>
&lt;p>其次，从作者的角度看。
把知识系统化地写出来，本身就是一种训练和挑战。会用，不等于会讲；“以为理解”和“真正理解”之间的差距，往往要靠能否清晰地输出内容来检验。写文章正是这样一种方式。对程序员来说，很多知识如果不尝试写下来，是很难发现自己理解的深度的。同时，好的文章也能在一定程度上提升个人的竞争力。&lt;/p>
&lt;p>当然，文章写得深还是浅，都会有对应的受众。只是我个人并不喜欢那种东拼西凑的内容——随便拿几张图一搜，发现好几篇文章几乎一模一样，这样的写作价值不大。但能把一件事讲得比别人更清楚，那本身就是一种能力。&lt;/p></description></item><item><title>Kotlin泛型和reified</title><link>https://opoojkk.com/posts/2025/09/03/kotlin-generics-and-reified/</link><pubDate>Wed, 03 Sep 2025 21:48:16 +0800</pubDate><guid>https://opoojkk.com/posts/2025/09/03/kotlin-generics-and-reified/</guid><description>&lt;blockquote>
&lt;p>Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:&lt;/p>
&lt;ul>
&lt;li>Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.&lt;/li>
&lt;li>Insert type casts if necessary to preserve type safety.&lt;/li>
&lt;li>Generate bridge methods to preserve polymorphism in extended generic types.&lt;/li>
&lt;/ul>
&lt;p>Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;a href="https://docs.oracle.com/javase/tutorial/java/generics/erasure.html">Type Erasure (The Java™ Tutorials &amp;gt; Learning the Java Language &amp;gt; Generics (Updated))&lt;/a>&lt;/p>
&lt;p>由于泛型在编译后会被擦除，如果没有定义边界，就会被替换为 &lt;code>Object&lt;/code>，因此在运行时无法获取具体的类型信息。Kotlin 的泛型实现同样遵循这一规则。&lt;/p>
&lt;p>当我们尝试写一个扩展函数来启动 Activity 时，就会遇到这个问题：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">inline&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">T&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nc">Activity&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">Activity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">block&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">Intent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Unit&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{})&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">intent&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">Intent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="k">class&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// IDE 会报错
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">block&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里会报错，是因为在运行时无法确定 &lt;code>T&lt;/code> 的具体类型，也就不知道要启动哪个 Activity。&lt;/p>
&lt;p>Kotlin 提供了 &lt;strong>&lt;code>reified&lt;/code>&lt;/strong> 关键字来解决这个问题，它允许在内联函数中保留泛型的实际类型，从而在运行时使用：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">inline&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="k">reified&lt;/span> &lt;span class="nc">T&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nc">Activity&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">Activity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">block&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">Intent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Unit&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{})&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">intent&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">Intent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="k">class&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">block&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>调用方式如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">Activity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startMainActivity&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">startActivity&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">MainActivity&lt;/span>&lt;span class="p">&amp;gt;()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>反编译成 Java 代码后，大致如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">startMainActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@NotNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Activity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">$this$startMainActivity&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Intrinsics&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">checkNotNullParameter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">$this$startMainActivity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;&amp;lt;this&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Intent&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">intent$iv&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Intent&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">$this$startMainActivity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MainActivity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">$this$startMainActivity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent$iv&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>可以看到，&lt;code>reified&lt;/code> 的作用是把泛型类型直接写进了代码中，从而在运行时可以获取具体的 Class 对象。&lt;/p>
&lt;hr>
&lt;p>需要注意的是，如果我们只是使用泛型实例的方法，并不需要具体类型信息，就不会有问题：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">T&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nc">Activity&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">finish&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">finish&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里编译器能够正常处理，因为我们并不是在运行时获取类型，而是调用泛型实例的成员方法。Java 的继承机制保证了 &lt;code>Activity&lt;/code> 的子类都可以访问 &lt;code>finish()&lt;/code> 方法，所以实例方法不会受类型擦除的影响。&lt;/p>
&lt;p>Java中的泛型在编译后是会被擦除的，如果没有定义边界，会被用Object取代，因此没有办法在运行时获取类的信息，Kotlin同理。&lt;/p>
&lt;p>当定义一个扩展方法启动activity时，也就拿不到真实的类型。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">inline&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">T&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nc">Activity&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">Activity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">block&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">Intent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Unit&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{})&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">intent&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">Intent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="k">class&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">block&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当这样写时，IDE会提示错误，因为不知道T具体的类型，也就不知道启动的是哪个Activity。&lt;/p>
&lt;p>Kotlin中新增了关键字&lt;code>reified&lt;/code>解决这种状况。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">inline&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="k">reified&lt;/span> &lt;span class="nc">T&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nc">Activity&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">Activity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">block&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">Intent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Unit&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{})&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">intent&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">Intent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="k">class&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">block&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>实际上，这种写法是将传入的类，写在了代码中。来调用该方法&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">Activity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">startMainActivity&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">startActivity&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">MainActivity&lt;/span>&lt;span class="p">&amp;gt;()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>借助反编译看对应的Java代码&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">startMainActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@NotNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Activity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">$this$startMainActivity&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Intrinsics&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">checkNotNullParameter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">$this$startMainActivity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;&amp;lt;this&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">$i$f$startActivity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Intent&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">intent$iv&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Intent&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">$this$startMainActivity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MainActivity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">var5&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">$this$startMainActivity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">startActivity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intent$iv&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当需要一个泛型的实例时，有些差别。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">T&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nc">Activity&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">finish&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">finish&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>上面的代码是可以正常编译执行的。和上面启动Activity的是不同的。上面的方法需要的是一个类型，如果不知道具体的类型就不知道要启动哪个Activity。这里执行的是泛型实例的一个方法，泛型只是把类型用边界类代替了，finish()方法Activity还是会提供的，Java继承的特性导致Activity的字类都可以访问到，也就是说实例方法是不受影响的。&lt;/p></description></item><item><title>flutter渲染和布局</title><link>https://opoojkk.com/posts/2025/08/31/flutter-rendering-and-layout/</link><pubDate>Sun, 31 Aug 2025 11:56:40 +0800</pubDate><guid>https://opoojkk.com/posts/2025/08/31/flutter-rendering-and-layout/</guid><description>&lt;blockquote>
&lt;p>原文：&lt;a href="https://docs.flutter.dev/resources/architectural-overview#rendering-and-layout">Flutter architectural overview | Flutter&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>本节描述了Flutter的渲染管道，即将一层层的Widgets层级转换为实际绘制在屏幕上的像素的系列步骤。&lt;/p>
&lt;h3 class="heading" id="flutter的渲染模型">
 Flutter的渲染模型&lt;span class="heading__anchor"> &lt;a href="#flutter%e7%9a%84%e6%b8%b2%e6%9f%93%e6%a8%a1%e5%9e%8b">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>你可能会好奇：如果Flutter是一个跨平台框架，它是如何实现和单平台框架接近的性能呢？&lt;/p>
&lt;p>首先了解传统Android应用的工作方式是很有帮助的。当进行绘制时，你会先调用Android框架的Java代码。Android系统库提供了一些组件，它们负责将内容绘制到一个&lt;code>Canvas&lt;/code>对象上，之后Android会调用&lt;a href="https://skia.org/">Skia&lt;/a>（一个用C/C++编写的图形引擎）通过CPU或GPU将内容绘制到设备上。&lt;/p>
&lt;p>跨平台框架&lt;em>通常&lt;/em>通过在底层的原生Android和iOS UI库之上创建一个抽象层来工作，试图平滑处理各个平台UI表示之间的不一致。应用代码通常用解释型语言（如JavaScript）编写，并且必须与Java语言的Android库或Objective-C语言的iOS系统库交互来呈现UI。所有这些会增加显著的开销，尤其是在应用的UI与逻辑有大量交互时。&lt;/p>
&lt;p>相比之下，Flutter通过尽量减少这些抽象层来提升性能。在其设计中，Flutter绕过原生的系统UI部件库，直接使用自己的插件化小组件集合（Widget）。Flutter的Dart代码会被编译为原生代码，并利用名为&lt;strong>Impeller&lt;/strong>的渲染引擎进行渲染。Impeller随应用一起提供，这使开发者即使在终端设备没有升级到最新系统版本时，也能依赖Flutter框架轻松推进应用性能的改进。这种方式同样适用于Flutter运行在其他原生平台上（如Windows或macOS）。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>注意&lt;/strong>
想了解Impeller支持哪些设备？请查看&lt;a href="https://flutter.dev/go/can-i-use-impeller">我可以使用Impeller吗？&lt;/a>。更多内容请访问&lt;a href="https://docs.flutter.dev/perf/impeller">Impeller 渲染引擎&lt;/a>。&lt;/p>
&lt;/blockquote>
&lt;h3 class="heading" id="从用户输入到gpu">
 从用户输入到GPU&lt;span class="heading__anchor"> &lt;a href="#%e4%bb%8e%e7%94%a8%e6%88%b7%e8%be%93%e5%85%a5%e5%88%b0gpu">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Flutter的渲染管道遵循的核心原则是&lt;strong>简单即高效&lt;/strong>。Flutter为数据流向系统的过程设计了一个简洁的管道，如下图所示：&lt;/p>
&lt;figure>
 &lt;img src="https://docs.flutter.dev/assets/images/docs/arch-overview/render-pipeline.png" alt="渲染管道示意图">&lt;/figure>&lt;p>接下来我们将更详细地了解其中一些阶段。&lt;/p>
&lt;hr>
&lt;h3 class="heading" id="构建阶段从widget到element">
 构建阶段：从Widget到Element&lt;span class="heading__anchor"> &lt;a href="#%e6%9e%84%e5%bb%ba%e9%98%b6%e6%ae%b5%e4%bb%8ewidget%e5%88%b0element">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>考虑以下一个展示组件层级的代码片段：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dart" data-lang="dart">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Container&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">color:&lt;/span> &lt;span class="n">Colors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">blue&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">child:&lt;/span> &lt;span class="n">Row&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">children:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Image&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">network&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;https://www.example.com/1.png&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">const&lt;/span> &lt;span class="n">Text&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;A&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当Flutter需要渲染这一片段时，它会调用&lt;code>build()&lt;/code>方法，该方法返回一棵基于当前应用状态的UI子树。构建过程中，&lt;code>build()&lt;/code>方法可以根据状态引入新的组件。例如，在上述代码片段中，&lt;code>Container&lt;/code>具有&lt;code>color&lt;/code> 和 &lt;code>child&lt;/code> 属性。查看&lt;a href="https://github.com/flutter/flutter/blob/02efffc134ab4ce4ff50a9ddd86c832efdb80462/packages/flutter/lib/src/widgets/container.dart#L401">Container 的源码&lt;/a>，可以发现当&lt;code>Color&lt;/code>不为空时，它会插入一个代表此颜色的&lt;code>ColoredBox&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dart" data-lang="dart">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">color&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">current&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ColoredBox&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nl">color:&lt;/span> &lt;span class="n">color&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nl">child:&lt;/span> &lt;span class="n">current&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>类似地，&lt;code>Image&lt;/code>和&lt;code>Text&lt;/code>组件在构建过程中可能会插入子组件，例如&lt;code>RawImage&lt;/code>和&lt;code>RichText&lt;/code>。因此真实的组件层级可能比代码表面上看起来更深，如下所示&lt;sup>&lt;a href="https://docs.flutter.dev/resources/architectural-overview?utm_source=chatgpt.com#fn2">[2]&lt;/a>&lt;/sup>：&lt;/p>
&lt;figure>
 &lt;img src="https://docs.flutter.dev/assets/images/docs/arch-overview/widgets.png" alt="小部件层级树示意图">&lt;/figure>&lt;p>这解释了为什么通过调试工具（如&lt;a href="https://docs.flutter.dev/tools/devtools/inspector">Flutter inspector&lt;/a>）检查组件树时，你可能看到的结构要比你代码中写出的深得多。&lt;/p>
&lt;p>在构建阶段，Flutter将代码中表达的组件翻译为对应的&lt;strong>Element 树&lt;/strong>，每个组件都有一个对应的Element实例。每个Element代表组件树某个位置上某个组件的特定实例。Element主要有两种类型：&lt;/p>
&lt;ul>
&lt;li>&lt;code>ComponentElement&lt;/code>：用于承载其他元素。&lt;/li>
&lt;li>&lt;code>RenderObjectElement&lt;/code>：参与布局或绘制阶段的元素。&lt;/li>
&lt;/ul>
&lt;figure>
 &lt;img src="https://docs.flutter.dev/assets/images/docs/arch-overview/widget-element.png" alt="Widget和Element关系示意图">&lt;/figure>&lt;p>&lt;code>RenderObjectElement&lt;/code>是其对应的Widget和底层&lt;code>RenderObject&lt;/code>之间的中介。&lt;/p>
&lt;p>任何组件的Element可以通过它的&lt;code>BuildContext&lt;/code>进行引用，&lt;code>BuildContext&lt;/code>是树中组件位置的句柄。例如，在调用&lt;code>Theme.of(context)&lt;/code>时使用的&lt;code>context&lt;/code>，就是传递给&lt;code>build()&lt;/code>方法的参数。&lt;/p>
&lt;p>由于Flutter的组件是不可变的（包括节点间的父/子关系），任何对组件树的更改（如将&lt;code>Text('A')&lt;/code>改成&lt;code>Text('B')&lt;/code>）都会返回一组新的组件对象。这并不意味着底层表示会完全重建。Element树从一个渲染帧到下一帧是持久化的，这在性能优化中起着重要作用。Flutter只会遍历发生变化的Widget，重新配置需要调整的Element树部分。&lt;/p>
&lt;hr>
&lt;h3 class="heading" id="布局和渲染">
 布局和渲染&lt;span class="heading__anchor"> &lt;a href="#%e5%b8%83%e5%b1%80%e5%92%8c%e6%b8%b2%e6%9f%93">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>在一个应用中，仅绘制单个组件的情况很少见。因此，任何UI框架的重要特性之一就是高效地布局整个组件树，确定每个元素在屏幕上的大小和位置。&lt;/p>
&lt;p>每个渲染树节点的基础类是&lt;a href="https://api.flutter.dev/flutter/rendering/RenderObject-class.html">&lt;code>RenderObject&lt;/code>&lt;/a>，它定义了一个抽象模型用于布局和绘制。这个模型非常通用：它并不限定维度的数量，甚至不固定使用直角坐标系（例如&lt;a href="https://dartpad.dev/?id=596b1d6331e3b9d7b00420085fab3e27">使用极坐标系统的示例&lt;/a>）。每个&lt;code>RenderObject&lt;/code>只了解它的父节点，但对子节点知之甚少，除了知道如何“访问”它们以及它们的约束条件。这种抽象使得&lt;code>RenderObject&lt;/code>能够应对多种用例。&lt;/p>
&lt;p>在构建阶段，Flutter会为Element树中的每个&lt;code>RenderObjectElement&lt;/code>创建或更新一个继承自&lt;code>RenderObject&lt;/code>的对象。&lt;code>RenderObject&lt;/code>是基础的渲染单位，例如：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://api.flutter.dev/flutter/rendering/RenderParagraph-class.html">&lt;code>RenderParagraph&lt;/code>&lt;/a>：用于渲染文本；&lt;/li>
&lt;li>&lt;a href="https://api.flutter.dev/flutter/rendering/RenderImage-class.html">&lt;code>RenderImage&lt;/code>&lt;/a>：用于渲染图像；&lt;/li>
&lt;li>&lt;a href="https://api.flutter.dev/flutter/rendering/RenderTransform-class.html">&lt;code>RenderTransform&lt;/code>&lt;/a>：对子组件进行变换后绘制。&lt;/li>
&lt;/ul>
&lt;figure>
 &lt;img src="https://docs.flutter.dev/assets/images/docs/arch-overview/trees.png" alt="Widget、Element 和 RenderObject 的区别">&lt;/figure>&lt;p>大多数Flutter组件由继承自&lt;code>RenderBox&lt;/code>的对象进行渲染，&lt;code>RenderBox&lt;/code>是二维笛卡尔空间中固定大小的渲染对象。&lt;code>RenderBox&lt;/code>提供了一个&lt;strong>盒约束模型&lt;/strong>，规定了每个组件在渲染时的最小和最大宽高。&lt;/p>
&lt;p>在布局时，Flutter会以深度优先的顺序遍历渲染树，并从父节点向子节点&lt;strong>向下传递大小约束&lt;/strong>。子节点在确定其大小时，必须遵守父节点提供的约束。子节点随后会通过向父节点 &lt;strong>向上传递大小&lt;/strong> 进行响应，并且约束在子节点中得到了应用。&lt;/p>
&lt;figure>
 &lt;img src="https://docs.flutter.dev/assets/images/docs/arch-overview/constraints-sizes.png" alt="约束向下传递，大小向上传递">&lt;/figure>&lt;p>在单次遍历树之后，每个对象已根据父节点的约束条件定义了自己的大小，并准备通过调用&lt;a href="https://api.flutter.dev/flutter/rendering/RenderObject/paint.html">&lt;code>paint()&lt;/code>&lt;/a>方法进行绘制。&lt;/p>
&lt;p>盒约束模型是一种强大的组件布局机制，其效率可达&lt;em>O(n)&lt;/em> ：&lt;/p>
&lt;ul>
&lt;li>父组件可以通过将最大和最小尺寸设置为相同的值来完全控制一个子组件的尺寸。例如，顶层渲染对象会将其子组件的尺寸约束限定为整个屏幕大小，（子组件可以选择如何利用该空间，例如将要渲染的内容居中显示）。&lt;/li>
&lt;li>父节点可以限制子节点的宽度，同时允许其对高度有一定的弹性（或相反）。一个实际的例子是流式文本，它可能需要适应水平约束但可以根据文本数量调整垂直方向。&lt;/li>
&lt;/ul>
&lt;p>即使在某些情况下子对象需要知道可用空间大小以决定内容的呈现方式，盒约束模型也能够正常工作。通过使用&lt;a href="https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html">&lt;code>LayoutBuilder&lt;/code>&lt;/a>组件，子对象可以检查传递下来的约束并依据约束决定其呈现方式，例如：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dart" data-lang="dart">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Widget&lt;/span> &lt;span class="n">build&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BuildContext&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">LayoutBuilder&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">builder:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">constraints&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">constraints&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">maxWidth&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="m">600&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kd">const&lt;/span> &lt;span class="n">OneColumnLayout&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kd">const&lt;/span> &lt;span class="n">TwoColumnLayout&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>更多关于约束与布局系统的信息和示例，请参考&lt;a href="https://docs.flutter.dev/ui/layout/constraints">理解约束&lt;/a>。&lt;/p>
&lt;p>所有&lt;code>RenderObject&lt;/code>的根节点是&lt;code>RenderView&lt;/code>，它代表整个渲染树的输出。当平台要求新帧被渲染时（例如由于&lt;a href="https://source.android.com/devices/graphics/implement-vsync">垂直同步（vsync）&lt;/a>或纹理解压缩/上传完成），会调用渲染树根节点&lt;code>RenderView&lt;/code>对象的&lt;code>compositeFrame()&lt;/code>方法。这会创建一个&lt;code>SceneBuilder&lt;/code>以触发场景更新。当场景完成后，&lt;code>RenderView&lt;/code>对象会将合成的场景传递给&lt;code>dart:ui&lt;/code>中的&lt;code>Window.render()&lt;/code>方法，并将控制权交给GPU以完成渲染。&lt;/p>
&lt;p>关于渲染管道中合成和光栅化阶段的详细内容超出了本高层次概述的范围，更多信息可参见&lt;a href="https://www.youtube.com/watch?v=UUfXWzp0-DU">Flutter渲染管线的视频演讲&lt;/a>。&lt;/p></description></item><item><title>Fragment的故事</title><link>https://opoojkk.com/posts/2025/08/24/the-story-of-fragment/</link><pubDate>Sun, 24 Aug 2025 15:04:08 +0800</pubDate><guid>https://opoojkk.com/posts/2025/08/24/the-story-of-fragment/</guid><description>&lt;p>Fragment 在原生 Android 开发中一直占有一席之地。像 &lt;code>ViewPager2&lt;/code>、&lt;code>Dialog&lt;/code> 都依赖 Fragment 来实现。虽然在实现底部导航（Bottom Navigation）时，&lt;code>ViewPager2&lt;/code> 配合 Fragment 有一些小坑，但这并不影响 Fragment 在 Android 体系中的地位。&lt;/p>
&lt;p>既然 Fragment 这么重要，就有必要深入了解它的奥秘。这篇文章就是一次探索。&lt;/p>
&lt;h2 class="heading" id="联系">
 联系&lt;span class="heading__anchor"> &lt;a href="#%e8%81%94%e7%b3%bb">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Fragment 的 &lt;code>add&lt;/code>、&lt;code>replace&lt;/code>、&lt;code>remove&lt;/code>、&lt;code>show&lt;/code>、&lt;code>hide&lt;/code> 等操作，全部依赖 &lt;code>FragmentManager&lt;/code>。要理解 Fragment，绕不开 FragmentManager。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/5XiaKxX.png" alt="Imgur">&lt;/figure>&lt;p>上图展示了 Activity 与 FragmentManager 的关系。&lt;code>FragmentActivity&lt;/code> 通过 &lt;code>FragmentController&lt;/code> 来更新 Fragment 状态，也会依赖 &lt;code>FragmentHostCallback&lt;/code> 来获取宿主信息（例如 Fragment 数量等）。&lt;/p>
&lt;p>接下来看看 &lt;code>FragmentHostCallback&lt;/code>。它其实是一个泛型类，泛型参数 &lt;code>&amp;lt;E&amp;gt;&lt;/code> 一般代表宿主类型（通常是 Activity）。它本身并不对泛型做具体逻辑处理，而是充当桥梁——比如 Fragment 在获取 &lt;code>LayoutInflater&lt;/code> 时需要一个 Context，这里传入的就是 Activity；Fragment 启动 Activity 的本质，也是通过宿主提供的方法来完成的。&lt;/p>
&lt;p>再说大家最熟悉的 &lt;code>FragmentManager&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>早期获取方式是 &lt;code>getFragmentManager()&lt;/code>，对应 &lt;code>android.app.Fragment&lt;/code>，现已废弃。&lt;/li>
&lt;li>现在统一推荐 &lt;code>getSupportFragmentManager()&lt;/code>，来自 AndroidX 的 &lt;code>FragmentActivity&lt;/code>，对应 &lt;code>androidx.fragment.app.Fragment&lt;/code>。&lt;/li>
&lt;/ul>
&lt;p>所以，不是“官方只是标记废弃”，而是 AndroidX 迁移之后，旧版被弃用，新版才是维护方向。&lt;/p>
&lt;p>在日常开发中，我们最常用的就是 &lt;code>add&lt;/code>、&lt;code>replace&lt;/code>、&lt;code>remove&lt;/code> 等方法。但实际上，FragmentManager 还负责：&lt;/p>
&lt;ul>
&lt;li>管理 Fragment 的生命周期更新&lt;/li>
&lt;li>保存和恢复状态&lt;/li>
&lt;li>权限请求、画中画支持&lt;/li>
&lt;li>Activity/Fragment 重建时的恢复&lt;/li>
&lt;/ul>
&lt;p>真正的核心远不止几个增删操作。&lt;/p>
&lt;h2 class="heading" id="fragmenttransaction">
 FragmentTransaction&lt;span class="heading__anchor"> &lt;a href="#fragmenttransaction">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>我们以一个常见的例子为起点：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">supportFragmentManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">beginTransaction&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">container&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">MyFragment&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">container_b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">MyFragment&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">commit&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里 &lt;code>beginTransaction()&lt;/code> 会先创建一个 &lt;code>FragmentTransaction&lt;/code> 实例。这个实例可以收集多个操作（Op），最后通过 &lt;code>commit&lt;/code> 一次性提交。&lt;/p>
&lt;p>&lt;code>add&lt;/code>、&lt;code>replace&lt;/code>、&lt;code>remove&lt;/code> 等方法本质上都会封装成一个个 &lt;code>Op&lt;/code>，记录操作类型和对应的 Fragment。真正执行是在调用 &lt;code>commit&lt;/code> 系列方法时。&lt;/p>
&lt;h2 class="heading" id="commit-系列方法">
 commit 系列方法&lt;span class="heading__anchor"> &lt;a href="#commit-%e7%b3%bb%e5%88%97%e6%96%b9%e6%b3%95">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>几个方法的区别如下：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>commit()&lt;/strong>
把事务加入队列，稍后在主线程安全时异步执行。如果此时状态已经保存（比如在 &lt;code>onSaveInstanceState&lt;/code> 之后），会抛出异常。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>commitAllowingStateLoss()&lt;/strong>
与 &lt;code>commit()&lt;/code> 类似，但不会因为状态已保存而报错，可能导致状态丢失。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>commitNow()&lt;/strong>
立即执行事务，且会强制在所有挂起的异步事务之前完成。但它不允许再提交异步事务，否则会冲突。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>commitNowAllowingStateLoss()&lt;/strong>
同上，只是允许在已保存状态后执行，可能造成状态丢失。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>这样看下来，Fragment 的背后其实是一套完整的状态机，由 &lt;code>FragmentManager&lt;/code> 统筹调度，&lt;code>FragmentHostCallback&lt;/code> 作为宿主桥梁，&lt;code>FragmentTransaction&lt;/code> 负责操作封装。我们日常写的一行 &lt;code>add()&lt;/code>，实际上都在这套复杂机制中被逐步拆解和执行。&lt;/p></description></item><item><title>举报和被举报的</title><link>https://opoojkk.com/posts/2025/08/23/reporting-and-being-reported/</link><pubDate>Sat, 23 Aug 2025 21:17:03 +0800</pubDate><guid>https://opoojkk.com/posts/2025/08/23/reporting-and-being-reported/</guid><description>&lt;p>最近遇到一件有意思的事。自疫情开始，我一直在听梁文道的音频节目《八分》，今年他推出了付费版本《八分半》，犹豫了一阵子后，我订阅了。&lt;/p>
&lt;p>一直以来相安无事，直到最近节目被全平台下架。有人猜测，是因为在蔡澜去世的那一期节目里，道长（梁文道）谈到了香港，提到了《苹果日报》的老板。我觉得这种说法可信度很高，因为如今的“爱国”已经到了空前的高度。&lt;/p>
&lt;p>节目是付费的，承诺至少有 80 期。截至下架时（不含番外），已经播出了 72 期，还差了 8 期。于是制作团队通过小宇宙发出调查问卷，询问有多少人希望退款，不再听剩下的节目，也不会有额外内容。奇怪的是，这份调查问卷居然也被举报，链接一度无法访问。随后在周六（2025.8.23）下午，制作团队又重新发出了一份。&lt;/p>
&lt;p>节目被举报已经让我很不解，更让我费解的是，连调查问卷也能被举报。我实在想不通，这样做是为了什么？是想让退款的朋友拿不到退款吗？还是说，单纯厌恶梁文道到这种程度？如果只是厌恶他，那举报调查问卷又和节目内容有什么关系呢？看到推送消息的时候，我只能无奈地笑了。&lt;/p>
&lt;p>最后我只能把“举报”理解为一种无法接纳他人观点的表现。就好像在某些人的脑子里，有一张列有关键字的清单：一有清单上的话题被讨论，就触发了什么开关。不光自己不能谈，其他人谈也是不被允许的。看来这期节目恰好命中了。可惜的是我听过了整个节目，却不觉得哪里又不妥的说法。&lt;/p>
&lt;p>播客我几乎只听这一档。我喜欢它的原因很简单：不是因为它告诉我对错，而是它让我反思。梁文道在很多问题上的角度和拆解方式独特又深刻，至少值得我学习。或许正是因为我在很多方面保有与他相近的看法，因此才会认同，这其中也包括一些平时并不容易被接受的观点。&lt;/p>
&lt;p>现如今的环境，越来越恶劣。许多方面都受到限制。又不说什么能谈、什么不能谈，于是为了避开这些所谓敏感内容，只得采用更严格的标准。甚至是改用缩写，还有说谜语。内容来源有限，能发声的媒体越来越少，互相抄来抄去。像在屎上雕花，结果就是今天的中文互联网，充斥着重复、浅薄、泛娱乐的内容，以及让人激情澎湃的口号。&lt;/p>
&lt;p>不知从什么时候开始，讨论变得格外敏感。今天讨论这个明星，明天讨论那个明星，这个综艺节目多好看等等。除此之外就是“爱国“。你可以放弃利益，会要求民族尊严，不容许伤害民族感情的事发生，和国家的方向一致。即使事一个为了生计发愁的人，也可以在国际关系上说得头头是道。&lt;/p>
&lt;p>可“爱国”到底是什么呢？你知道吗？是保卫政府吗？那辛亥革命、内战又是什么？如果不是，什么才是爱国？这个国家难道不是由我们这些人一起组成的吗，我们不正是国家的一份子吗？&lt;/p>
&lt;p>还有另一个现象：当糟糕的事情发生时，很多人不是去分辨是非，而是直接指责对方，好像自己永远是受害者。&lt;/p>
&lt;p>当真相揭露时，还得有人去‘闹’才行。原本难以推动的事，一旦闹起来，结果往往就真会不同。在这样的氛围里，对与错本身似乎不再重要，重要的只是‘能不能闹出效果’。&lt;/p></description></item><item><title>AI入门指南：从零开始理解人工智能</title><link>https://opoojkk.com/posts/2025/08/21/ai-basics-at-a-glance/</link><pubDate>Thu, 21 Aug 2025 21:06:58 +0800</pubDate><guid>https://opoojkk.com/posts/2025/08/21/ai-basics-at-a-glance/</guid><description>&lt;blockquote>
&lt;p>&lt;strong>人工智能&lt;/strong>（英语：artificial intelligence，缩写为AI&lt;sup>(英语文本)&lt;/sup>），指由人造机器所展现的智能。通常人工智能是指用普通计算机程序来呈现人类智能的技术。该词也指出研究这样的智能系统是否能够实现，以及如何实现。&lt;/p>
&lt;/blockquote>
&lt;p>这是维基百科对AI的解释，简单来说AI就是具备类似人类解决问题能力的技术。像是翻译、写代码等等等等。&lt;/p>
&lt;p>在 AI 的发展过程中，&lt;strong>Transformer&lt;/strong> 是一个极为重要的里程碑。目前的大模型，如 GPT、Claude，都是基于 Transformer 架构构建的。&lt;/p>
&lt;p>Transformer 最初是 Google 为机器翻译任务提出的模型结构。翻译并不是逐字对应的，比如要翻译“知识就是力量”，单独看“知”并不能确定含义，必须结合前后词语才能得到准确的翻译。这种机制就是&lt;strong>自注意力机制&lt;/strong>，它让模型在处理某个词时，能够“关注”到上下文中的其他词。下面是一张形象的动图：&lt;/p>
&lt;figure>
 &lt;img src="https://3.bp.blogspot.com/-3Pbj_dvt0Vo/V-qe-Nl6P5I/AAAAAAAABQc/z0_6WtVWtvARtMk0i9_AtLeyyGyV6AI4wCLcB/s640/nmt-model-fast.gif" alt="&amp;ldquo;来自Google学术，参考第一条引用&amp;rdquo;">&lt;/figure>&lt;p>AI 生成内容的过程，本质上就是一种“文字接龙”。在训练时，文本会被编码成向量，输入的句子会被分词成一个个词元（token），也就是常说的 &lt;strong>token&lt;/strong>。需要注意的是，token 并不等同于词语，不同的分词方式可能会得到不同的 token。&lt;/p>
&lt;p>由于生成过程是基于概率拼接的“文字接龙”，产出的内容有时可能并不存在或不真实，这种现象被称为 &lt;strong>AI 的幻觉（hallucination）&lt;/strong> 。另外，AI 的训练数据通常截止到某一时间点，因此知识存在时效性。如果希望模型能够使用最新信息，可以通过&lt;strong>检索增强生成（Retrieval-Augmented Generation，RAG）&lt;/strong> ，让模型在回答问题时参考外部资料，从而提高正确性和时效性。&lt;/p>
&lt;h3 class="heading" id="ai-生成中的幻觉-vs-rag-解决方案">
 AI 生成中的幻觉 vs RAG 解决方案&lt;span class="heading__anchor"> &lt;a href="#ai-%e7%94%9f%e6%88%90%e4%b8%ad%e7%9a%84%e5%b9%bb%e8%a7%89-vs-rag-%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>情况&lt;/th>
 &lt;th>现象（幻觉）&lt;/th>
 &lt;th>RAG 的作用&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>过时的知识&lt;/strong>&lt;/td>
 &lt;td>模型的训练数据截止到某个时间，回答可能已经过时。&lt;br />例如：问“2025年奥运会在哪举办？”，模型可能回答错误。&lt;/td>
 &lt;td>通过&lt;strong>检索外部资料&lt;/strong>（数据库、搜索引擎、文档），获取最新信息，再结合模型生成，确保答案更及时。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>编造事实&lt;/strong>&lt;/td>
 &lt;td>模型会“接龙”拼出听起来合理但实际上不存在的信息。&lt;br />例如：捏造一篇不存在的论文或一本虚构的书。&lt;/td>
 &lt;td>模型先检索相关文档，再基于真实资料生成回答，避免无中生有。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>缺乏细节&lt;/strong>&lt;/td>
 &lt;td>模型知道大概方向，但具体数据或内容错误。&lt;br />例如：回答某个法律条款时给出错误的条文号。&lt;/td>
 &lt;td>从&lt;strong>权威数据源&lt;/strong>中检索原始文本，再由模型组织语言，确保细节准确。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>模糊回答&lt;/strong>&lt;/td>
 &lt;td>当训练数据不足时，模型可能给出模糊甚至前后矛盾的回答。&lt;/td>
 &lt;td>RAG 可以补充来自&lt;strong>专业文档&lt;/strong>或&lt;strong>知识库&lt;/strong>的上下文，使回答更具体、更可靠。&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>训练 AI 一般分为两个主要阶段：&lt;strong>预训练（pre-training）和微调（fine-tuning）&lt;/strong> 。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>预训练（pre-training）&lt;/strong> ：向模型输入海量未标注的文本（通常会经过人工或自动方式过滤掉有害和重复内容），可以理解为给 AI &lt;strong>灌输知识&lt;/strong>。这种基于&lt;strong>原始文本（raw text）&lt;/strong> 、不依赖人工标注的训练方式叫做&lt;strong>自监督学习（self-supervised learning）&lt;/strong> 。经过这一阶段得到的模型称为&lt;strong>基础模型（base model）&lt;/strong> 。它已经掌握了大量知识，但在回答问题时往往不符合人类预期，因为它并不知道你是在“提问”。这时，**提示词（prompt）**就能发挥关键作用。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>监督微调（supervised fine-tuning, SFT）&lt;/strong> ：在基础模型的基础上，使用少量带人工标注的数据进行训练，让模型学会按照人类的指令来回答问题。这个过程叫做&lt;strong>监督学习（supervised learning）&lt;/strong> ，结果是一个能更好理解人类意图的模型。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>基于人类反馈的强化学习（RLHF）&lt;/strong> ：单纯的监督微调仍然有限，因为人工标注的数据不可能覆盖所有情况。于是引入 RLHF：&lt;/p>
&lt;ol>
&lt;li>让多个回答由模型生成；&lt;/li>
&lt;li>人类对这些回答进行打分，训练一个&lt;strong>奖励模型（reward model）&lt;/strong> ；&lt;/li>
&lt;li>模型在生成答案时会根据奖励模型的反馈进行优化。
这样，模型不仅能理解人类指令，还能更好地对齐人类的价值观和偏好。&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ol>
&lt;li>&lt;a href="https://research.google/blog/a-neural-network-for-machine-translation-at-production-scale/">A Neural Network for Machine Translation, at Production Scale&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/ching-i/transformer-attention-is-all-you-need-c7967f38af14">Transformer — Attention Is All You Need&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/338817680">Transformer模型详解（图解最完整版）&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>LeakCanary检测到内存泄漏之后发生了什么</title><link>https://opoojkk.com/posts/2025/08/17/what-happens-after-leakcanary-detects-a-memory-leak/</link><pubDate>Sun, 17 Aug 2025 07:48:03 +0800</pubDate><guid>https://opoojkk.com/posts/2025/08/17/what-happens-after-leakcanary-detects-a-memory-leak/</guid><description>&lt;h2 class="heading" id="检测">
 检测&lt;span class="heading__anchor"> &lt;a href="#%e6%a3%80%e6%b5%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>LeakCanary 是 Android 开发中最常用的内存泄漏检测工具之一。由于在 dump 内存快照时需要 &lt;strong>挂起所有线程&lt;/strong>，这会带来明显的卡顿，因此它通常只在开发和调试阶段启用。&lt;/p>
&lt;p>涉及到的类型有弱引用、引用队列和一个map。像是这样：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">watchedObjects&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">MutableMap&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">KeyedWeakReference&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">ConcurrentHashMap&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>弱引用不会影响对象是否被回收，当弱引用被清理之后，弱引用会被添加进关联的弱引用队列。Leak Canary用到的弱引用类型&lt;code>KeyedWeakReference&lt;/code>是&lt;code>WeakReference&lt;/code>的一个子类，增加了key。弱引用的key也是&lt;code>watchedObjects&lt;/code>的key，于是对象、弱引用、map几个关键信息就联系起来。&lt;/p>
&lt;p>利用带有 key 的弱引用和引用队列，可以识别出哪些对象已经被回收。因为 key 是唯一的，所以一旦弱引用进入引用队列，就能从 watchedObjects 中移除对应的键值对。这样，watchedObjects 中剩下的条目就&lt;strong>代表仍未被回收&lt;/strong>的对象，它们就是潜在的泄漏候选。&lt;/p>
&lt;p>LeakCanary 借助 &lt;code>Lifecycle&lt;/code> 回调，在 &lt;code>onDestroy()&lt;/code> 时机清理引用队列中（已经被回收的）弱引用，并将对应条目从 &lt;code>watchedObjects&lt;/code> 中移除。理论上，正常销毁的对象在这一步之后就不会再出现在 &lt;code>watchedObjects&lt;/code> 中。随后 LeakCanary 会延迟一段时间（默认 5s）再次检查，如果仍然发现有 Activity 的弱引用留存，说明对象未能被 GC，存在内存泄漏，于是触发 &lt;strong>dump 内存快照&lt;/strong>，并开始分析引用链路。&lt;/p>
&lt;h2 class="heading" id="dump内存快照">
 dump内存快照&lt;span class="heading__anchor"> &lt;a href="#dump%e5%86%85%e5%ad%98%e5%bf%ab%e7%85%a7">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>这一步本质上是调用 Android 提供的 API 来生成 hprof 文件，相对比较“机械”，没有太多自定义逻辑。&lt;/p>
&lt;h2 class="heading" id="分析内存快照">
 分析内存快照&lt;span class="heading__anchor"> &lt;a href="#%e5%88%86%e6%9e%90%e5%86%85%e5%ad%98%e5%bf%ab%e7%85%a7">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>dump后发送消息，开始分析内存快照。&lt;/p>
&lt;ol>
&lt;li>解析拿到header，包含时间戳、版本、标识(分辨字符串、对象、stack traces)信息;&lt;/li>
&lt;li>找到和几种GC Root类型的关联的record，实际上是找到每种类型开始的index，存在一个数据类中一并返回;&lt;/li>
&lt;li>找出泄露对象的id，默认实现先找出KeyedWeakReference类型的实例，再找出判定认为已经泄露的对象;&lt;/li>
&lt;li>计算泄漏对象到 GC Roots 的最短路径，从而定位出内存泄漏的根因链路。&lt;/li>
&lt;/ol>
&lt;p>GC Root类型：&lt;/p>
&lt;ul>
&lt;li>HprofRecordTag.ROOT_JNI_GLOBAL,&lt;/li>
&lt;li>HprofRecordTag.ROOT_JAVA_FRAME,&lt;/li>
&lt;li>HprofRecordTag.ROOT_JNI_LOCAL,&lt;/li>
&lt;li>HprofRecordTag.ROOT_MONITOR_USED,&lt;/li>
&lt;li>HprofRecordTag.ROOT_NATIVE_STACK,&lt;/li>
&lt;li>HprofRecordTag.ROOT_STICKY_CLASS,&lt;/li>
&lt;li>HprofRecordTag.ROOT_THREAD_BLOCK,&lt;/li>
&lt;li>HprofRecordTROOT_THREAD_OBJECT,&lt;/li>
&lt;li>HprofRecordTag.ROOT_JNI_MONITOR&lt;/li>
&lt;/ul>
&lt;h3 class="heading" id="找最短路径">
 找最短路径&lt;span class="heading__anchor"> &lt;a href="#%e6%89%be%e6%9c%80%e7%9f%ad%e8%b7%af%e5%be%84">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>前面步骤大多是按照 hprof 格式和 tag 规则解析数据，而“最短路径”这一环节则更关键，决定了我们如何解释泄漏对象与 GC Roots 的关系，因此单独展开。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findPathsFromGcRoots&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">PathFindingResults&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">enqueueGcRoots&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">shortestPathsToLeakingObjects&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">mutableListOf&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">ReferencePathNode&lt;/span>&lt;span class="p">&amp;gt;()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">visitingQueue&lt;/span>&lt;span class="err">@&lt;/span> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">queuesNotEmpty&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">node&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">poll&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">leakingObjectIds&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">contains&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">objectId&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">shortestPathsToLeakingObjects&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Found all refs, stop searching (unless computing retained size)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">shortestPathsToLeakingObjects&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">leakingObjectIds&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">computeRetainedHeapSize&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">listener&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">onEvent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">StartedFindingDominators&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="nd">@visitingQueue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">heapObject&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">graph&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findObjectById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">objectId&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">objectIdNotFound&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">IllegalArgumentException&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// This should never happen (a heap should only have references to objects that exist)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// but when it does happen, let&amp;#39;s at least display how we got there.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">throw&lt;/span> &lt;span class="n">RuntimeException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">graph&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">invalidObjectIdErrorMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">objectIdNotFound&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">objectReferenceReader&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">read&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">heapObject&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">forEach&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">reference&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">newNode&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">ChildNode&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">objectId&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">reference&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">valueObjectId&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">parent&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">node&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lazyDetailsResolver&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">reference&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">lazyDetailsResolver&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">enqueue&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">node&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">newNode&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">isLowPriority&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">reference&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isLowPriority&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">isLeafObject&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">reference&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isLeafObject&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">PathFindingResults&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">shortestPathsToLeakingObjects&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">visitTracker&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">Dominated&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">visitTracker&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dominatorTree&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>LeakCanary中查找是有两个队列，分高低优先级，先从高优先级中找出路径，高优先级队为空才会从低优先级队列中找。
贴上关键代码：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">poll&lt;/span>&lt;span class="p">():&lt;/span>&lt;span class="n">ReferencePathNode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">visitingLast&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="p">!&lt;/span>&lt;span class="n">toVisitQueue&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isEmpty&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">removedNode&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">toVisitQueuepoll&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">toVisitSet&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">remove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">removedNodeobjectId&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">removedNode&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">visitingLast&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">removedNode&lt;/span> &lt;span class="p">=&lt;/span>&lt;span class="n">toVisitLastQueue&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">poll&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">toVisitLastSet&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">remov&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">removedNode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">objectId&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">removedNode&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>toVisitQueuepoll和toVisitLastQueue即是高低优先级的队列。&lt;/p>
&lt;p>队列中添加元素是怎么执行的呢？&lt;/p>
&lt;p>同样贴上代码：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">return&lt;/span> &lt;span class="n">sortedGcRoots&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">graph&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">asSequence&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">mapNotNull&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">heapObject&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">gcRoot&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">when&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">gcRoot&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Note: in sortedGcRoots we already filter out any java frame that has an associated
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// thread. These are the remaining ones (shouldn&amp;#39;t be any, this is just in case).
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">JavaFrame&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">GcRootReference&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gcRoot&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">isLowPriority&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">matchedLibraryLeak&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">is&lt;/span> &lt;span class="n">JniGlobal&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">referenceMatcher&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">when&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">heapObject&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">is&lt;/span> &lt;span class="n">HeapClass&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">jniGlobalReferenceMatchers&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">heapObject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">is&lt;/span> &lt;span class="n">HeapInstance&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">jniGlobalReferenceMatchers&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">heapObject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">instanceClassName&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">is&lt;/span> &lt;span class="n">HeapObjectArray&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">jniGlobalReferenceMatchers&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">heapObject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">arrayClassName&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">is&lt;/span> &lt;span class="n">HeapPrimitiveArray&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">jniGlobalReferenceMatchers&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">heapObject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">arrayClassName&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">referenceMatcher&lt;/span> &lt;span class="o">!is&lt;/span> &lt;span class="n">IgnoredReferenceMatcher&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">referenceMatcher&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">LibraryLeakReferenceMatcher&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">GcRootReference&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gcRoot&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">isLowPriority&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">matchedLibraryLeak&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">referenceMatcher&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">GcRootReference&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gcRoot&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">isLowPriority&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">matchedLibraryLeak&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">GcRootReference&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gcRoot&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">isLowPriority&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">matchedLibraryLeak&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>返回值是 GcRootReference，其中的 isLowPriority 字段决定了该引用进入高优先级还是低优先级队列。规则是：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&amp;ldquo;JNI Global + 匹配已知库泄漏 → 标记为低优先级（通常是外部库的已知问题，不需要第一时间关注）；&amp;rdquo;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&amp;ldquo;JNI Global + 非已知库 → 高优先级；&amp;rdquo;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&amp;ldquo;其他 GC Root → 默认高优先级。&amp;rdquo;&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>匹配规则参考：&lt;code>shark:shark.AndroidReferenceMatchers.kt&lt;/code>&lt;/p>
&lt;p>在 &lt;code>findPathsFromGcRoots&lt;/code> 中可以看到 &lt;code>enqueue&lt;/code> 的调用，这是因为 LeakCanary 使用 &lt;strong>BFS（广度优先搜索）&lt;/strong> 来遍历引用链。BFS 的特性保证了：第一次找到的泄漏对象路径就是最短路径，因此无需回溯或额外计算。&lt;/p></description></item><item><title>Google Play 企业开发者账号注册踩坑记录</title><link>https://opoojkk.com/posts/2025/08/16/google-play-enterprise-developer-registration-guide/</link><pubDate>Sat, 16 Aug 2025 19:59:28 +0800</pubDate><guid>https://opoojkk.com/posts/2025/08/16/google-play-enterprise-developer-registration-guide/</guid><description>&lt;h2 class="heading" id="注册企业账号">
 注册企业账号&lt;span class="heading__anchor"> &lt;a href="#%e6%b3%a8%e5%86%8c%e4%bc%81%e4%b8%9a%e8%b4%a6%e5%8f%b7">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>注册企业账号的第一步仍然是注册一个 Google 账号，这个过程比较简单。随后再进入 Google Play Console 注册开发者账号时，需要在个人开发者和企业开发者之间选择。网上关于个人账号的经验文章很多，这里主要记录企业开发者的流程和坑点。&lt;/p>
&lt;h2 class="heading" id="邓白氏编码d-u-n-s">
 邓白氏编码（D-U-N-S）&lt;span class="heading__anchor"> &lt;a href="#%e9%82%93%e7%99%bd%e6%b0%8f%e7%bc%96%e7%a0%81d-u-n-s">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>企业账号与个人账号最大的区别在于：&lt;strong>企业必须提供邓白氏编码（D-U-N-S Number）&lt;/strong> 。
邓白氏编码可以理解为企业在全球范围内的唯一身份识别码，由 Dun &amp;amp; Bradstreet 管理，被广泛用于 Google、Apple、联合国、欧盟等组织的企业认证。&lt;/p>
&lt;p>目前如果直接在邓白氏官方为 “Google Play” 申请，系统会引导至某个上海的合作公司，需要支付大约 9000 元/年的费用。网络上曾经有人提到过免费的官方申请入口，但现在已经不可行了。&lt;/p>
&lt;p>那有没有免费的方式？
答案是有的，可以“曲线救国”：&lt;strong>通过苹果开发者平台注册时申请 D-U-N-S 编码&lt;/strong>。一旦通过，Google Play 也能识别同一个企业的 D-U-N-S 编码。&lt;/p>
&lt;p>关于付费和免费的差异，邓白氏销售的说法是：编码对每个企业都是唯一的，区别在于付费申请能提升信息可信度，更容易被一些平台认可。&lt;/p>
&lt;h2 class="heading" id="支付注册费用">
 支付注册费用&lt;span class="heading__anchor"> &lt;a href="#%e6%94%af%e4%bb%98%e6%b3%a8%e5%86%8c%e8%b4%b9%e7%94%a8">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Google Play 开发者账号本身需要支付 &lt;strong>25 美元&lt;/strong>注册费，这一步相对宽松。
注意事项：&lt;/p>
&lt;ul>
&lt;li>付款时需要填写卡片信息（卡号、有效期、安全码和账单地址），地址必须和卡片一致。&lt;/li>
&lt;li>不要求持卡人必须是企业相关人员。&lt;/li>
&lt;li>除银联外几乎所有卡组织都支持，可用外币卡，也可以用虚拟卡。&lt;/li>
&lt;li>如果没有外币卡，可以通过浦发信用卡 APP「喜大普奔」申请虚拟信用卡完成支付。&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="填写企业信息">
 填写企业信息&lt;span class="heading__anchor"> &lt;a href="#%e5%a1%ab%e5%86%99%e4%bc%81%e4%b8%9a%e4%bf%a1%e6%81%af">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>企业信息如实填写即可。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>邮箱&lt;/strong>：可以使用非企业邮箱，但需要额外的验证码验证；用企业邮箱则更方便。&lt;/li>
&lt;li>&lt;strong>联系方式&lt;/strong>：推荐填写手机号，因为后续需要接收短信验证码完成验证。&lt;/li>
&lt;/ul>
&lt;p>此外，在创建应用之前，Google 还要求验证企业官网。最常见的方式是：在网站服务器的 header 或特定路径下增加 Google 指定的内容，操作简单，基本能顺利完成。&lt;/p></description></item><item><title>Kotlin的枚举、密封类和密封接口</title><link>https://opoojkk.com/posts/2025/08/08/enum-sealed-class-and-sealed-interface/</link><pubDate>Fri, 08 Aug 2025 20:29:13 +0800</pubDate><guid>https://opoojkk.com/posts/2025/08/08/enum-sealed-class-and-sealed-interface/</guid><description>&lt;h2 class="heading" id="枚举">
 枚举&lt;span class="heading__anchor"> &lt;a href="#%e6%9e%9a%e4%b8%be">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>下面涉及到的语言均是Kotlin，理论上Kotlin和Java在枚举中具有的功能是相同的。&lt;/p>
&lt;p>说到枚举，就有先入为主的概念，认为枚举只能定义两个简单的状态，像是下面这样：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">enum&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">State&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">OPEN&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CLOSE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>其实不是这样的，这只是最最基本的用法，把枚举当做常量使用了。&lt;/p>
&lt;h3 class="heading" id="变量">
 变量&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%98%e9%87%8f">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>枚举中是可以定义变量的。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">enum&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">State&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">val&lt;/span> &lt;span class="py">value&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">Int&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Success&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">Fail&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Success&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在Kotlin语言中，如果构造方法中出现val了，这个类就会增加一个变量。枚举也是一样的，上面就为枚举类增加了一个value的变量，而且是val的，不可以修改值的。&lt;/p>
&lt;p>既然可以是val，是不是也可以是var呢，是可以的。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Success&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Success&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">value&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Success&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>打印的结果如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="m">2&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="方法">
 方法&lt;span class="heading__anchor"> &lt;a href="#%e6%96%b9%e6%b3%95">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>枚举中不只可以有变量，同样也可以有方法。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">enum&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">State&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">var&lt;/span> &lt;span class="py">value&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Success&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">Failed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fun&lt;/span> &lt;span class="nf">greeting&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;hello, this is &lt;/span>&lt;span class="si">$value&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Success&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">greeting&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Fail&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">greeting&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>打印出结果：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">hello, this is &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hello, this is &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>可能已经开始混乱了，这和我用的枚举不一样啊。是不一样，枚举不只是一个常量，它也是个类。&lt;/p>
&lt;p>不妨转换一下思路，枚举(State)首先是一个类，枚举中的每个常量(Success, Fail)也是一个&lt;strong>类型&lt;/strong>，是只有一个实例的类型，而这个实例就是它本身。为了要保证每一个类型都只被创建一次，构造方法是被限制的，没有办法创建出相同类型的第二个实例，还是那上面的例子，Success就是一种类型，这个类型的实例是Success，也只有这么一个实例。&lt;/p>
&lt;p>类型会有方法的重写。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">enum&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">State&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">var&lt;/span> &lt;span class="py">value&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Success&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">greeting&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;success never greeting&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="n">Fail&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">open&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">greeting&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;hello, this is &lt;/span>&lt;span class="si">$value&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>输出就变成了&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">success never greeting
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hello, this is &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>⚠️枚举类中的常量是需要首先声明的。也就是要先声明Success和Fail，然后才可以是声明greeting方法。&lt;/p>
&lt;p>枚举类适用的场景就是根据它有限个常量的特性，常用到的像是标识状态，也可以用作实现状态模式（前面提到可以重写方法）。&lt;/p>
&lt;h2 class="heading" id="密封类">
 密封类&lt;span class="heading__anchor"> &lt;a href="#%e5%af%86%e5%b0%81%e7%b1%bb">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>密封类开始是Kotlin的概念，Java也在JDK17中加入。&lt;/p>
&lt;p>密封类和平时用到的类差别在继承上。密封类是允许继承的，只允许文件内的类继承。也因此能够保证子类个数是有限的，只有文件内的那些。密封类更像是一个抽象类，是不能在外部直接使用定义了的密封类的，一定要访问内部的子类。&lt;/p>
&lt;p>之类的类型可以是abstract class, open class, class, object几种类型。密封类中的子类同样可以实现其他的接口，这和其他类型的子类没有什么差异。&lt;/p>
&lt;p>对应的使用场景和枚举类似，涉及到状态的，可以考虑用它。&lt;/p>
&lt;p>为什么用它不是用枚举呢，它可以比枚举更灵活，密封类的子类可以是抽象类，或者open class，这些类型的子类又可以继续继承。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">sealed&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">State&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">open&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">Success&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">State&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">abstract&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">Error&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">State&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="m">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">class&lt;/span> &lt;span class="nc">NetError&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Error&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">class&lt;/span> &lt;span class="nc">ServerError&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Error&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">object&lt;/span> &lt;span class="nc">Loading&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">State&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="密封接口">
 密封接口&lt;span class="heading__anchor"> &lt;a href="#%e5%af%86%e5%b0%81%e6%8e%a5%e5%8f%a3">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>密封接口又有什么用呢？&lt;/p>
&lt;p>密封和接口看起来好像是两个没有什么关联的呀，密封保证封闭，接口是开放的。&lt;/p>
&lt;p>理解密封接口绕个弯可能会更容易些，密封类中像是上面的例子，State类明明没有任何变量和方法，很像是接口，这么看果然顺了很多。&lt;/p>
&lt;p>密封接口为什么设计成允许外部继承的，是我不理解的。Kotlin中的类默认是final不允许继承的，使用密封接口强制不允许继承吗，我只想到这一种可能。如果不是这样，那和普通的接口又有什么差别呢，难道是给Java用的？Kotlin的协程已经不可以在Java中使用了，而且Java中密封接口的实现类是可以继续继承的。&lt;/p></description></item><item><title>单例模式设计对比分析</title><link>https://opoojkk.com/posts/2025/08/03/comparison-of-several-singleton-patterns/</link><pubDate>Sun, 03 Aug 2025 19:57:31 +0800</pubDate><guid>https://opoojkk.com/posts/2025/08/03/comparison-of-several-singleton-patterns/</guid><description>&lt;h2 class="heading" id="懒汉式">
 懒汉式&lt;span class="heading__anchor"> &lt;a href="#%e6%87%92%e6%b1%89%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">INSTANCE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">Singleton&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getInstance&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">INSTANCE&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// kotlin
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">object&lt;/span> &lt;span class="nc">KotlinSingleton&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>优势：&lt;/p>
&lt;ol>
&lt;li>实现简单；&lt;/li>
&lt;li>没有同步开销；&lt;/li>
&lt;/ol>
&lt;p>不足：&lt;/p>
&lt;ol>
&lt;li>类加载时创建，可能造成资源浪费；&lt;/li>
&lt;/ol>
&lt;p>线程安全：&lt;/p>
&lt;p>JVM保障（静态变量只在加载时创建一次）。&lt;/p>
&lt;h2 class="heading" id="饿汉式">
 饿汉式&lt;span class="heading__anchor"> &lt;a href="#%e9%a5%bf%e6%b1%89%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">instance&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">Singleton&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getInstance&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">instance&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">instance&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">instance&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// kotlin
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">LazySingleton&lt;/span> &lt;span class="k">private&lt;/span> &lt;span class="k">constructor&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">companion&lt;/span> &lt;span class="k">object&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">instance&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LazySingleton&lt;/span> &lt;span class="k">by&lt;/span> &lt;span class="n">lazy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">LazyThreadSafetyMode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SYNCHRONIZED&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LazySingleton&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>优势：&lt;/p>
&lt;ol>
&lt;li>减少了不必要的资源浪费&lt;/li>
&lt;/ol>
&lt;p>不足：&lt;/p>
&lt;ol>
&lt;li>首次访问有同步开销&lt;/li>
&lt;/ol>
&lt;p>线程安全：&lt;/p>
&lt;p>需要依赖同步锁实现线程安全。&lt;/p>
&lt;h2 class="heading" id="双重检查式">
 双重检查式&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%8c%e9%87%8d%e6%a3%80%e6%9f%a5%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">volatile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">instance&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">Singleton&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getInstance&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">instance&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 第一次检查&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">instance&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 第二次检查&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">instance&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">instance&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// kotlin
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">Singleton&lt;/span> &lt;span class="k">private&lt;/span> &lt;span class="k">constructor&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">companion&lt;/span> &lt;span class="k">object&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 使用 @Volatile 注解确保可见性和禁止指令重排序
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nd">@Volatile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">var&lt;/span> &lt;span class="py">instance&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Singleton&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fun&lt;/span> &lt;span class="nf">getInstance&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">Singleton&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 第一次检查：避免不必要的同步
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">instance&lt;/span> &lt;span class="o">?:&lt;/span> &lt;span class="n">synchronized&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 第二次检查：防止多个线程同时通过第一次检查后重复创建
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">instance&lt;/span> &lt;span class="o">?:&lt;/span> &lt;span class="n">Singleton&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">also&lt;/span> &lt;span class="p">{&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">instance&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">it&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>优势：&lt;/p>
&lt;ol>
&lt;li>延迟加载&lt;/li>
&lt;li>只有第一次访问时有同步开销&lt;/li>
&lt;/ol>
&lt;p>不足：&lt;/p>
&lt;ol>
&lt;li>实现复杂&lt;/li>
&lt;/ol>
&lt;p>线程安全：&lt;/p>
&lt;p>volatile防止指令重排序。&lt;/p>
&lt;h2 class="heading" id="静态内部类">
 静态内部类&lt;span class="heading__anchor"> &lt;a href="#%e9%9d%99%e6%80%81%e5%86%85%e9%83%a8%e7%b1%bb">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">Singleton&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Holder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">INSTANCE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Singleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getInstance&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Holder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">INSTANCE&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// kotlin
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">Singleton&lt;/span> &lt;span class="k">private&lt;/span> &lt;span class="k">constructor&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 伴生对象作为静态成员的容器
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">companion&lt;/span> &lt;span class="k">object&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 使用属性访问器实现延迟初始化
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">instance&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Singleton&lt;/span> &lt;span class="k">by&lt;/span> &lt;span class="n">lazy&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nc">Holder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">INSTANCE&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 私有静态内部类（Holder模式）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">private&lt;/span> &lt;span class="k">object&lt;/span> &lt;span class="nc">Holder&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 由 JVM 保证线程安全的初始化
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">INSTANCE&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">Singleton&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>优势：&lt;/p>
&lt;ol>
&lt;li>实现简单&lt;/li>
&lt;li>没有同步开销&lt;/li>
&lt;/ol>
&lt;p>不足：&lt;/p>
&lt;ol>
&lt;li>无法初始化参数&lt;/li>
&lt;/ol>
&lt;p>线程安全：&lt;/p>
&lt;p>JVM保障，同饿汉式。&lt;/p>
&lt;h2 class="heading" id="枚举单例">
 枚举单例&lt;span class="heading__anchor"> &lt;a href="#%e6%9e%9a%e4%b8%be%e5%8d%95%e4%be%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">enum&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">EnumSingleton&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">INSTANCE&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">doSomething&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>优势：&lt;/p>
&lt;ol>
&lt;li>方式反射、序列化破坏单例；&lt;/li>
&lt;/ol>
&lt;p>不足：&lt;/p>
&lt;ol>
&lt;li>可嫩造成不必要的资源浪费；&lt;/li>
&lt;/ol>
&lt;p>线程安全：&lt;/p>
&lt;p>由JVM保证（类似静态单例初始化）。&lt;/p>
&lt;h2 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>&lt;strong>类型&lt;/strong>&lt;/th>
 &lt;th>&lt;strong>实现方式&lt;/strong>&lt;/th>
 &lt;th>&lt;strong>语言&lt;/strong>&lt;/th>
 &lt;th>&lt;strong>线程安全&lt;/strong>&lt;/th>
 &lt;th>&lt;strong>初始化时机&lt;/strong>&lt;/th>
 &lt;th>&lt;strong>优势&lt;/strong>&lt;/th>
 &lt;th>&lt;strong>不足&lt;/strong>&lt;/th>
 &lt;th>&lt;strong>线程安全保证机制&lt;/strong>&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>非懒加载&lt;/strong>&lt;/td>
 &lt;td>饿汉式&lt;/td>
 &lt;td>Java&lt;/td>
 &lt;td>✅&lt;/td>
 &lt;td>类加载时&lt;/td>
 &lt;td>实现简单，无同步开销&lt;/td>
 &lt;td>可能造成资源浪费&lt;/td>
 &lt;td>JVM类加载机制&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;/td>
 &lt;td>枚举单例&lt;/td>
 &lt;td>Java&lt;/td>
 &lt;td>✅&lt;/td>
 &lt;td>类加载时&lt;/td>
 &lt;td>绝对防止反射/序列化破坏&lt;/td>
 &lt;td>不够灵活&lt;/td>
 &lt;td>JVM枚举特性保证&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;/td>
 &lt;td>Object声明&lt;/td>
 &lt;td>Kotlin&lt;/td>
 &lt;td>✅&lt;/td>
 &lt;td>首次访问类时&lt;/td>
 &lt;td>语法级支持，简洁安全&lt;/td>
 &lt;td>非严格懒加载&lt;/td>
 &lt;td>编译器生成静态内部类&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>懒加载&lt;/strong>&lt;/td>
 &lt;td>同步方法&lt;/td>
 &lt;td>Java&lt;/td>
 &lt;td>✅&lt;/td>
 &lt;td>首次调用时&lt;/td>
 &lt;td>延迟加载节省资源&lt;/td>
 &lt;td>每次调用都同步，性能差&lt;/td>
 &lt;td>synchronized方法锁&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;/td>
 &lt;td>双重检查锁(DCL)&lt;/td>
 &lt;td>Java&lt;/td>
 &lt;td>✅&lt;/td>
 &lt;td>首次调用时&lt;/td>
 &lt;td>高性能（仅首次同步）&lt;/td>
 &lt;td>实现复杂，需volatile&lt;/td>
 &lt;td>volatile+同步块禁止指令重排&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;/td>
 &lt;td>静态内部类&lt;/td>
 &lt;td>Java&lt;/td>
 &lt;td>✅&lt;/td>
 &lt;td>首次调用时&lt;/td>
 &lt;td>无同步开销，优雅实现&lt;/td>
 &lt;td>无法传递初始化参数&lt;/td>
 &lt;td>JVM类加载机制&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;/td>
 &lt;td>lazy(SYNCHRONIZED)&lt;/td>
 &lt;td>Kotlin&lt;/td>
 &lt;td>✅&lt;/td>
 &lt;td>首次属性访问时&lt;/td>
 &lt;td>灵活控制初始化时机&lt;/td>
 &lt;td>首次访问有同步开销&lt;/td>
 &lt;td>委托内部使用同步锁&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;/td>
 &lt;td>lazy(PUBLICATION)&lt;/td>
 &lt;td>Kotlin&lt;/td>
 &lt;td>⚠️有条件&lt;/td>
 &lt;td>首次属性访问时&lt;/td>
 &lt;td>允许多线程并行初始化&lt;/td>
 &lt;td>可能多次初始化&lt;/td>
 &lt;td>CAS操作保证最终一致性&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;/td>
 &lt;td>lazy(NONE)&lt;/td>
 &lt;td>Kotlin&lt;/td>
 &lt;td>❌&lt;/td>
 &lt;td>首次属性访问时&lt;/td>
 &lt;td>完全无同步开销&lt;/td>
 &lt;td>仅限单线程环境&lt;/td>
 &lt;td>无保护机制&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div></description></item><item><title>发布android库到Maven Central</title><link>https://opoojkk.com/posts/2025/07/27/publishing-an-android-library-on-maven-central/</link><pubDate>Sun, 27 Jul 2025 15:08:28 +0800</pubDate><guid>https://opoojkk.com/posts/2025/07/27/publishing-an-android-library-on-maven-central/</guid><description>&lt;p>平时写一些小东西时有一部分代码是很常用到的，总不能每次都写一遍，每次都复制也太麻烦了，最简单的方式就发布到maven仓库，用拉取远程仓库的方式解决。因此，废了一顿劲发布到maven central中。也刚好记录下来，帮助有需要的人。&lt;/p>
&lt;h2 class="heading" id="准备">
 准备&lt;span class="heading__anchor"> &lt;a href="#%e5%87%86%e5%a4%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在发布之前，需要先考虑发不到哪里，Android工程中常用的maven仓库有四个：&lt;/p>
&lt;ul>
&lt;li>Google&lt;/li>
&lt;li>Maven Central&lt;/li>
&lt;li>Jitpack&lt;/li>
&lt;li>Jcenter&lt;/li>
&lt;/ul>
&lt;p>随着Jcenter宣布设为只读代码库，可以选择的只剩了三家，加上Google是官方自己用的，实际上也只有两家。&lt;/p>
&lt;p>Jitpack发布相对简单，之间也尝试过，有需要可以参考：
&lt;a href="https://opoojkk.com/posts/2023/10/06/android-gradle-library-publish-to-jitpack/" title="Android library模块publish到JitPack完整流程">Android library模块publish到JitPack完整流程&lt;/a>。今天的是发布到maven central的。主要原因是可以用自己的域名- -&lt;/p>
&lt;p>发布的流程很清晰：构建-签名-上传&lt;/p>
&lt;h3 class="heading" id="maven-central注册">
 Maven Central注册&lt;span class="heading__anchor"> &lt;a href="#maven-central%e6%b3%a8%e5%86%8c">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>发布到Maven Central首先要做的当然是注册账号了，中&lt;a href="https://central.sonatype.com/">maven central repository&lt;/a>右上角点击Sign In，后选择注册或者用Google、Github登录即可。&lt;/p>
&lt;p>注册好之后是添加namespace，这样别人才能用com.example.lib:1.0.0这样的形式拉下来你的仓库。&lt;/p>
&lt;h3 class="heading" id="生成token">
 生成token&lt;span class="heading__anchor"> &lt;a href="#%e7%94%9f%e6%88%90token">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>进到&lt;a href="https://central.sonatype.com/account">Setup Token-Based Authentication&lt;/a>，点击&amp;quot;Generate User Token&amp;quot;，按照提示生成一个新的token，保存下来等下需要用到。&lt;/p>
&lt;h3 class="heading" id="添加namespace">
 添加namespace&lt;span class="heading__anchor"> &lt;a href="#%e6%b7%bb%e5%8a%a0namespace">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>这里有两种选择，github提供的和自己的域名。如果用github提供的，你的github用户名是yourname，那namespace就是io.github.yourname。io.github这部分是固定的，你只需要修改yourname这部分，按照流程继续往下走就可以了。&lt;/p>
&lt;p>如果用自己的域名，只需要在域名的DNS中添加一条Txt类型的记录，内容和maven central提示的相同即可。&lt;/p>
&lt;h2 class="heading" id="构建和发布">
 构建和发布&lt;span class="heading__anchor"> &lt;a href="#%e6%9e%84%e5%bb%ba%e5%92%8c%e5%8f%91%e5%b8%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>代码的部分就略过了，还没有那么大能耐能教别人怎么写代码。写好之后就是构建和发布了。&lt;/p>
&lt;p>在一个android项目中，一个模块有两种类型，要么是可以运行的应用，要么是提供某些逻辑功能的库。两者的差别只在使用的Gradle插件是&lt;code>com.android.application&lt;/code>（应用）还是&lt;code>com.android.library&lt;/code>（库）上。想要发布用到的当然是库这一类型的。&lt;/p>
&lt;p>除了指明是哪种类型的模块的插件，还需要一些其他的。发布用到&lt;code>maven-publish&lt;/code>，它是Gradle提供的发布插件。&lt;/p>
&lt;p>在发布到maven central时，是要要求有签名的，Gradle签名的插件一般是&lt;code>signing&lt;/code>，不同于应用的签名，这里的是用GPG，简单来说是一种非对称加密工具，详细的推荐阅读&lt;a href="https://www.ruanyifeng.com/blog/2013/07/gpg.html">GPG入门教程&lt;/a>。别担心，不会引入太多新的内容，只用到创建和输出密钥、分发到服务器中这三个操作。&lt;/p>
&lt;p>或者，你也可以用&lt;code>com.vanniktech.maven.publish&lt;/code>这个Gradle插件，包含了发布、签名这两个部分，你只需要导入这一个就可以了。我在使用时，最新的版本是&lt;code>0.34.0&lt;/code>。&lt;/p>
&lt;h3 class="heading" id="配置">
 配置&lt;span class="heading__anchor"> &lt;a href="#%e9%85%8d%e7%bd%ae">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>你需要做的只是在想要发布的模块build.gradle.kts文件加上发布和签名两部分配置。因为有相关的插件依赖，所以还要加上依赖。&lt;/p>
&lt;p>插件：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">plugins&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">alias&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">plugins&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">library&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">alias&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">plugins&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">kotlin&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 添加这一行
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;com.vanniktech.maven.publish&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">version&lt;/span> &lt;span class="s2">&amp;#34;0.34.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>发布：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">mavenPublishing&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configure&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AndroidSingleVariantLibrary&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// the published variant
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">variant&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;release&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// whether to publish a sources jar
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sourcesJar&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// whether to publish a javadoc jar
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">publishJavadocJar&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">coordinates&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;com.opoojkk&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;androidkt&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">defaultConfig&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">versionName&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pom&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">description&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;common android kotlin extensions&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inceptionYear&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;2025&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">url&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;https://github.com/opoojkk/androidkt/&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">licenses&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">license&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;The Apache License, Version 2.0&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">url&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;http://www.apache.org/licenses/LICENSE-2.0.txt&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">distribution&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;http://www.apache.org/licenses/LICENSE-2.0.txt&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">developers&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">developer&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;lxx&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;lxx&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">url&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;https://github.com/opoojkk/&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scm&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">url&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;https://github.com/opoojkk/androidkt/&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">connection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;scm:git:git://github.com/opoojkk/androidkt.git&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">developerConnection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;scm:git:ssh://git@github.com/opoojkk/androidkt.git&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>上面的这些都是我的配置，需要吧涉及到的地址改成你的。尤其是coordinates中的，这会影响到你发布到的namespace。&lt;/p>
&lt;p>签名：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">signing&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">useInMemoryPgpKeys&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">findProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;signingKeyId&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">toString&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">?:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">findProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;signingKey&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">toString&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">?:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">findProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;signingPassword&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">toString&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">?:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span> &lt;span class="c1">// 确保密码不为 null
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sign&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">publishing&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">publications&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>除了这些还有些，你还需要在gradle.properties中添加下配置.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gradle" data-lang="gradle">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 发布到maven central
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">mavenCentralPublishing&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 对所有publication签名
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">signAllPublications&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 自动发布，否则要在maven central手动发布
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">mavenCentralAutomaticPublishing&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 从maven central哪来的tname
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">mavenCentralUsername&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">xxxxxx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 从maven central哪来的token
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">mavenCentralPassword&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 用来签名的GPG keyid
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">signingKeyId&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">xxxxxxxx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// GPG私钥，注意包含包含开始和结尾&amp;#34;===&amp;#34;和空行的部分全部都是，写成一行，讲换行改成&amp;#34;/n&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">signingKey&lt;/span>&lt;span class="o">=...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 设置的密码
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">signingPassword&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">xxxxxx&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="执行">
 执行&lt;span class="heading__anchor"> &lt;a href="#%e6%89%a7%e8%a1%8c">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>最后一步，执行gralde命令：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gradle" data-lang="gradle">&lt;span class="line">&lt;span class="cl">&lt;span class="o">./&lt;/span>&lt;span class="n">gradlew&lt;/span> &lt;span class="n">publishToMavenCentral&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>打完收工。&lt;/p>
&lt;h3 class="heading" id="gpg命令">
 GPG命令&lt;span class="heading__anchor"> &lt;a href="#gpg%e5%91%bd%e4%bb%a4">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>涉及到的GPG命令：&lt;/p>
&lt;p>创建：
&lt;code>gpg --gen-key&lt;/code>&lt;/p>
&lt;p>创建之后需要拿到keyid才能继续执行后面的操作，获取keyid的命令：&lt;code>gpg --list-keys --keyid-format short&lt;/code>。&lt;/p>
&lt;p>你会得到和下面类似的：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pub rsa4096/34567890 2025-07-26 &lt;span class="o">[&lt;/span>SCE&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="m">1231231231231231231231231231231234567890&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">uid &lt;span class="o">[&lt;/span> 绝对 &lt;span class="o">]&lt;/span> lxx &lt;span class="o">(&lt;/span>androidkt&lt;span class="o">)&lt;/span> &amp;lt;hi@opoojkk.com&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>1231231231231231231231231231231234567890就是你的keyid。一般都用短id，也就是后八位34567890。&lt;/p>
&lt;p>输出密钥：
&lt;code>gpg --armor --output public-key.txt --export {keyid}&lt;/code>&lt;/p>
&lt;p>执行后，你会得到很长的一串字符，这就是你的私钥。&lt;/p>
&lt;p>发布keyid到服务器：
&lt;code>gpg --keyserver keyserver.ubuntu.com {keyid}&lt;/code>&lt;/p>
&lt;p>有三个服务器可选，差不多&amp;hellip;&lt;/p>
&lt;ul>
&lt;li>keyserver.ubuntu.com&lt;/li>
&lt;li>keys.openpgp.org&lt;/li>
&lt;li>pgp.mit.edu&lt;/li>
&lt;/ul>
&lt;p>这些在&lt;B>GPG入门教程&lt;/B>中都是有的。&lt;/p>
&lt;h2 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="踩坑">
 踩坑&lt;span class="heading__anchor"> &lt;a href="#%e8%b8%a9%e5%9d%91">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ol>
&lt;li>签名
&lt;ol>
&lt;li>签名用了很久才配置好，开始用导出的pgp私钥文件路径，结果不行。试过读出文字，也不行。中间还改了好多种配置，最后把全部内容放上去才好。&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>keyid一定要发布到server！&lt;/li>
&lt;li>不用在build.gradle中自己加maven仓库，替你试过了，不好使&amp;hellip;.&lt;/li>
&lt;/ol>
&lt;h3 class="heading" id="步骤">
 步骤&lt;span class="heading__anchor"> &lt;a href="#%e6%ad%a5%e9%aa%a4">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>最后总结一下步骤，以防看着迷糊。&lt;/p>
&lt;ol>
&lt;li>注册maven central账号，添加namespace，生成token&lt;/li>
&lt;li>生成pgp密钥，发布keyid到服务器&lt;/li>
&lt;li>gradle添加插件、发布配置&lt;/li>
&lt;li>执行gradle脚本发布&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://central.sonatype.org/publish/publish-portal-ossrh-staging-api/#plugins-tested-for-compatibility">Publishing By Using the Portal OSSRH Staging API⚓&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://central.sonatype.org/publish/requirements/gpg/">GPG&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Fragment重建过程详解</title><link>https://opoojkk.com/posts/2025/07/20/what-happens-to-a-fragment-during-recreate/</link><pubDate>Sun, 20 Jul 2025 14:09:12 +0800</pubDate><guid>https://opoojkk.com/posts/2025/07/20/what-happens-to-a-fragment-during-recreate/</guid><description>&lt;p>当Activity的配置发生变化或者低内存被回收后，会触发Activity的重建。可以通过重写&lt;code>onSaveInstanceState&lt;/code>和&lt;code>onRestoreInstanceState&lt;/code>实现重建时的保存和恢复逻辑。&lt;/p>
&lt;p>Fragment的本质上是对View的封装，为其增加了生命周期的支持，因此能够在一个Activity中添加多个Fragment。Fragment重建也是依赖宿主Activity的。现在，当我们创建Activity实例时，看到的往往是继承自&lt;code>AppCompatActivity&lt;/code>的，&lt;code>AppCompatActivity&lt;/code>到Activity之间隔了很多代。&lt;/p>
&lt;!-- 在2018年的Google I/O大会上，Google宣布support库重构进Androidx中。 -->
&lt;h2 class="heading" id="联系">
 联系&lt;span class="heading__anchor"> &lt;a href="#%e8%81%94%e7%b3%bb">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>和Fragment关系最密切的，是一个叫FragmentActivity的。它的里面有一个名叫mFragments的，实际上是FragmentController类型的一个实例。&lt;/p>
&lt;p>FragmentController中，有两个关键类完成Fragment的逻辑，一个是FragmentManager实现Fragment的add、replace、remove。另一个是FragmentStateManager，它实现的是更新Fragment的状态，让Fragment执行对应的生命周期方法，还有一个对当前主题很重要的，执行保存状态也是由它发起，通知Fragment执行实现的。&lt;/p>
&lt;h2 class="heading" id="保存">
 保存&lt;span class="heading__anchor"> &lt;a href="#%e4%bf%9d%e5%ad%98">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Activity的重建和Fragment时怎么联系起来的呢？&lt;/p>
&lt;p>在Activity中的重建方法我们知道是onSaveInstanceState和onResotreInstanceState，但是如果在FragmentActivity中找，是找不到这个两个方法的，一直到父类ComponentActivity中才能看到。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@CallSuper&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">override&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fun&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">onSaveInstanceState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">outState&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lifecycle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LifecycleRegistry&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lifecycle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LifecycleRegistry&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">currentState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">CREATED&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onSaveInstanceState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">outState&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">savedStateRegistryController&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">performSave&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">outState&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>和Fragment的秘密只有可能在父类或者savedStateRegistryController中，父类帮大家看过了，只有一些更新状态的逻辑，再有就到Activity类了。只可能是savedStateRegistryController完成的了。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * An interface for an owner of this [SavedStateRegistry] to perform state saving, it will call
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * all registered providers and merge with unconsumed state.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param outBundle SavedState in which to place a saved state
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@MainThread&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">internal&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fun&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">performSave&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">outBundle&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SavedState&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">inState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">savedState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">restoredState&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">putAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">it&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">provider&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">keyToProviders&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">putSavedState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">provider&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">saveState&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">inState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">read&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">isEmpty&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">})&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">outBundle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">write&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">putSavedState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SAVED_COMPONENTS_KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">inState&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>看着注释，十有八九就是我们要找的了，这个&lt;code>putSavedState&lt;/code>是最后保存下来的，遍历的是keyToProviders，只要找到Fragment和keyToProviders的关系就能联系起来了。&lt;/p>
&lt;p>再看FragmentActivity，初始化时有这么一段代码：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">getSavedStateRegistry&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">registerSavedStateProvider&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LIFECYCLE_TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">markFragmentsCreated&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mFragmentLifecycleRegistry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">handleLifecycleEvent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ON_STOP&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当真的以为找到了的时候，发现事情不对。刚刚还说Fragment相关的内容都在FragmentController里，这里面也没有用到FragmentController啊。&lt;/p>
&lt;p>对啊&amp;hellip;这样找不到，那就换个方式，正着不行，反着来，从Fragment开始找。&lt;/p>
&lt;p>当Fragment保存状态时，也会执行&lt;code>onSaveInstanceState&lt;/code>方法，得到一些列保存的状态和对应的值。&lt;/p>
&lt;p>因此能顺利的找到FragmentManager中保存的方法&lt;code>saveAllStateInternal&lt;/code>。只要不断找调用的方法，就能找到了。 巧的是，在FragmentManager绑定FragmentController时，向keyToProviders中添加了一个键值对。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">SavedStateRegistry&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">registry&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">SavedStateRegistryOwner&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mHost&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">getSavedStateRegistry&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">registry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">registerSavedStateProvider&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SAVED_STATE_KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">saveAllStateInternal&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>好家伙，这不就是刚才没对上的，Activity和Fragment总算对接上了。&lt;/p>
&lt;p>问题又来了，怎么保存呢所有Fragment呢？继续看&lt;code>saveAllStateInternal&lt;/code>方法吧，都在里面呢。在开始没有讲，一个Fragment对应一个FragmentStateManager，因此FragmentStateManager执行保存实际上就是用了Fragment的保存方法。&lt;/p>
&lt;p>比如活跃的Fragment：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="err">#&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FragmentStateManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">saveState&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stateBundle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">INITIALIZING&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// We never even got to ATTACHED, but we could still have some state&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// set by setInitialSavedState so we&amp;#39;ll add that to our initial Bundle&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mSavedFragmentState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stateBundle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">putAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mSavedFragmentState&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Save the library state associated with the Fragment&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">FragmentState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FragmentState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stateBundle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">putParcelable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FRAGMENT_STATE_KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Save the user state associated with the Fragment&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">INITIALIZING&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">performSaveInstanceState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isEmpty&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stateBundle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">putBundle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SAVED_INSTANCE_STATE_KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mDispatcher&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">dispatchOnFragmentSaveInstanceState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">savedStateRegistryState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mSavedStateRegistryController&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">performSave&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">savedStateRegistryState&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">savedStateRegistryState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isEmpty&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stateBundle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">putBundle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">REGISTRY_STATE_KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">savedStateRegistryState&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childFragmentManagerState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mChildFragmentManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">saveAllStateInternal&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">childFragmentManagerState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isEmpty&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stateBundle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">putBundle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CHILD_FRAGMENT_MANAGER_KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childFragmentManagerState&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mView&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">saveViewState&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mSavedViewState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stateBundle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">putSparseParcelableArray&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">VIEW_STATE_KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mSavedViewState&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mSavedViewRegistryState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stateBundle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">putBundle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">VIEW_REGISTRY_STATE_KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mSavedViewRegistryState&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mArguments&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stateBundle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">putBundle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ARGUMENTS_KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mArguments&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stateBundle&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="恢复">
 恢复&lt;span class="heading__anchor"> &lt;a href="#%e6%81%a2%e5%a4%8d">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>恢复时同样从Activity开始，先是Activity的生命周期方法：onCreate、onStart然后是onRestoreInstanceState。
恢复Fragment流程和保存大致是相似的，不过顺序是相反的，就不再重复。&lt;/p>
&lt;p>倒是有个问题不知道你有没有印象，创建Activity时抛出的不能初始化Fragment是怎么回事呢？&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;span class="lnt">94
&lt;/span>&lt;span class="lnt">95
&lt;/span>&lt;span class="lnt">96
&lt;/span>&lt;span class="lnt">97
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// FragmentStateManager.java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Recreate a FragmentStateManager from a FragmentState instance, instantiating
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * a new Fragment from the {@link FragmentFactory}.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param dispatcher Dispatcher for any lifecycle callbacks triggered by this class
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param fragmentStore FragmentStore handling all Fragments
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param classLoader ClassLoader used to instantiate the Fragment
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param fragmentFactory FragmentFactory used to instantiate the Fragment
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param state Bundle used to restore the state correctly
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@SuppressWarnings&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;deprecation&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">FragmentStateManager&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FragmentLifecycleCallbacksDispatcher&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dispatcher&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FragmentStore&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fragmentStore&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ClassLoader&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">classLoader&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FragmentFactory&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fragmentFactory&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mDispatcher&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dispatcher&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mFragmentStore&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fragmentStore&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Instantiate the fragment&amp;#39;s library states in FragmentState&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">FragmentState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getParcelable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FRAGMENT_STATE_KEY&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">instantiate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fragmentFactory&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">classLoader&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mSavedFragmentState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Instantiate the fragment&amp;#39;s arguments&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">arguments&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getBundle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ARGUMENTS_KEY&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arguments&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">arguments&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setClassLoader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">classLoader&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setArguments&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arguments&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FragmentManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isLoggingEnabled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">VERBOSE&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">v&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Instantiated fragment &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mFragment&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// FragmentState.java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Instantiates the Fragment from this state.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@SuppressWarnings&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;deprecation&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Fragment&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">instantiate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FragmentFactory&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fragmentFactory&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ClassLoader&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">classLoader&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Fragment&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fragmentFactory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">instantiate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">classLoader&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mClassName&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mWho&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mWho&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mFromLayout&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mFromLayout&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mRestored&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mFragmentId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mFragmentId&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mContainerId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mContainerId&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mTag&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mTag&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mRetainInstance&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mRetainInstance&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mRemoving&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mRemoving&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mDetached&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mDetached&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mHidden&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mHidden&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mMaxState&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">values&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">mMaxLifecycleState&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mTargetWho&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mTargetWho&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mTargetRequestCode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mTargetRequestCode&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mUserVisibleHint&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mUserVisibleHint&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fragment&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// FragmentFactory.java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Create a new instance of a Fragment with the given class name. This uses
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@link #loadFragmentClass(ClassLoader, String)} and the empty
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * constructor of the resulting Class by default.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param classLoader The default classloader to use for instantiation
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param className The class name of the fragment to instantiate.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @return Returns a new fragment instance.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @throws Fragment.InstantiationException If there is a failure in instantiating
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * the given fragment class. This is a runtime exception; it is not
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * normally expected to happen.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fragment&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">instantiate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ClassLoader&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">classLoader&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">className&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Class&lt;/span>&lt;span class="o">&amp;lt;?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fragment&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cls&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">loadFragmentClass&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">classLoader&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">className&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cls&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getConstructor&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">newInstance&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lang&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">InstantiationException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">InstantiationException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Unable to instantiate fragment &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">className&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;: make sure class name exists, is public, and has an&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; empty constructor that is public&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">IllegalAccessException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">InstantiationException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Unable to instantiate fragment &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">className&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;: make sure class name exists, is public, and has an&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; empty constructor that is public&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">NoSuchMethodException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">InstantiationException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Unable to instantiate fragment &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">className&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;: could not find Fragment constructor&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">InvocationTargetException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fragment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">InstantiationException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Unable to instantiate fragment &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">className&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;: calling Fragment constructor caused an exception&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>所以创建Fragment(DialogFragment)最好只定义无参的构造方法，需要的参数通过Bundle传过去，这样重建的时候能创建出来，传过去的参数也能保存下来，重建后状态都是正常的。&lt;/p></description></item><item><title>Kotlin的fun interface</title><link>https://opoojkk.com/posts/2025/07/13/kotlin-fun-interface/</link><pubDate>Sun, 13 Jul 2025 19:14:19 +0800</pubDate><guid>https://opoojkk.com/posts/2025/07/13/kotlin-fun-interface/</guid><description>&lt;p>当我们写Java时有这么一种情况，需要用到回调，但是回调中只有一个方法， 这时候的写法可以更简便，举个例子：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">SAMJava&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">[]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 写法1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BlockJava&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">invoke&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// do something&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BlockJava&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">blockJava&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">blockJava&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">invoke&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">interface&lt;/span> &lt;span class="nc">BlockJava&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">invoke&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>上面是Java中传递匿名对象最最普通的写法，换用lambda的形式可以写成：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 写法2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">execute&lt;/span>&lt;span class="p">(()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// do something&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>看起来干净了很多，这就是SAM（Single Abstract Method）。要说有什么作用，就是让代码看起来很干净。这也是有限制的，只能是接口类中有一个方法需要实现的时候，当接口的中有两个方法就不行了，只能老老实实用的用方法1.&lt;/p>
&lt;p>这都是在Java中，Kotlin中有些不同。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">object&lt;/span> &lt;span class="nc">SAMKt&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@JvmStatic&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fun&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Array&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">&amp;gt;)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 写法1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">object&lt;/span> &lt;span class="err">: &lt;/span>&lt;span class="nc">BlockKotlin&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">invoke&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// do something
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fun&lt;/span> &lt;span class="nf">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blockJava&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">BlockKotlin&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">blockJava&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">interface&lt;/span> &lt;span class="nc">BlockKotlin&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fun&lt;/span> &lt;span class="nf">invoke&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="c1">// void fiinish();
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>匿名对象的形式当然是可以的，如果想换成Kotlin中lamdbas的写法，代码检查就是提示错误。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 写法2，报错
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">execute&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// do something
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Kotlin中能不能呢，能。但有点不同，你需要声明接口是fun interface:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">interface&lt;/span> &lt;span class="n">BlockKotlin&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fun&lt;/span> &lt;span class="nf">invoke&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="c1">// void fiinish();
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这时候还会提示你可以写成了写法2。
&lt;br>
除此之外，还有一种写法。我们知道Kotlin是有高阶函数的，高阶函数可以做这事。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fun&lt;/span> &lt;span class="nf">execute2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">block&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Unit&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">block&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">execute2&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这样也是没问题的。&lt;/p>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/17913409/what-is-a-sam-type-in-java">What is a &amp;lsquo;SAM type&amp;rsquo; in Java?&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>协变与逆变解析</title><link>https://opoojkk.com/posts/2025/06/29/covariance-and-contravariance/</link><pubDate>Sun, 29 Jun 2025 11:38:04 +0800</pubDate><guid>https://opoojkk.com/posts/2025/06/29/covariance-and-contravariance/</guid><description>&lt;h2 class="heading" id="泛型">
 泛型&lt;span class="heading__anchor"> &lt;a href="#%e6%b3%9b%e5%9e%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>泛型的作用是能对不同的类型抽离相同的逻辑，写成模板类和方法，以此减少重复代码。&lt;/p>
&lt;p>但是，我们知道在Java和Kotlin中的泛型只在编译期间生效，运行时都是Object。&lt;/p>
&lt;p>在一般的使用场景中是没有问题的，像是这样：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">class&lt;/span> &lt;span class="nc">Animal&lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Dog&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Animal&lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Cat&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Animal&lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Animal&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">animals&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayList&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">animals&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Dog&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">animals&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Cat&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>因为类型是相同的，一切都没有问题。&lt;/p>
&lt;p>Java是面向对象的语言，因此ArrayList是可以赋值给List的，那List&lt;Animal>能不能接收List&lt;Dog>和List&lt;Cat>的实例呢？&lt;/p>
&lt;p>答案是不能，还是以为泛型擦除。试想一下，加入可以赋值，等到取元素的时候怎么知道是什么类型的呢？类型是Dog？有没有Cat类型的呢？这就很麻烦。&lt;/p>
&lt;p>Java和Kotlin又是怎么解决的呢？就是协变和逆变。&lt;/p>
&lt;h2 class="heading" id="协变">
 协变&lt;span class="heading__anchor"> &lt;a href="#%e5%8d%8f%e5%8f%98">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>当像下面这样声明时就可以赋值了。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Dog&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dogs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayList&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">dogs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Dog&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Animal&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">animals&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dogs&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dog换成Cat也是一样的，这时候是允许将Animal的子类放进去的，因为能保证放进去一定是Animal类型的。&lt;/p>
&lt;p>这就是协变。&lt;/p>
&lt;h2 class="heading" id="逆变">
 逆变&lt;span class="heading__anchor"> &lt;a href="#%e9%80%86%e5%8f%98">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>如果我想向动物的合集中添加Cat。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">class&lt;/span> &lt;span class="nc">Pekingese&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Dog&lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">addDog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">super&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Dog&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dogs&lt;/span>&lt;span class="p">){&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">dogs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Dog&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Dog&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dogs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayList&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">addDog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dogs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Animal&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">animals&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayList&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">addDog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">animals&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Cat&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cats&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayList&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// addDog(cats);编译不过&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Pekingese&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pekingeses&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayList&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// addDog(cats);编译不过&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>只允许某一个类的超类添加进去，这就是逆变了。&lt;/p></description></item><item><title>LiveData与StateFlow对比分析</title><link>https://opoojkk.com/posts/2025/06/29/the-difference-between-livedata-and-stateflow/</link><pubDate>Sun, 29 Jun 2025 10:41:03 +0800</pubDate><guid>https://opoojkk.com/posts/2025/06/29/the-difference-between-livedata-and-stateflow/</guid><description>&lt;p>之前写过LiveData的内部实现&lt;a href="https://opoojkk.com/posts/2024/11/10/jetpack-livedata/" title="LiveData">LiveData&lt;/a>，Jetpack中还有另一个组件StateFlow，两者作用非常类似，有什么差别呢？&lt;/p>
&lt;blockquote>
&lt;p>StateFlow、Flow 和 LiveData
StateFlow 和 LiveData 具有相似之处。两者都是可观察的数据容器类，并且在应用架构中使用时，两者都遵循相似模式。&lt;/p>
&lt;p>但请注意，StateFlow 和 LiveData 的行为确实有所不同：&lt;/p>
&lt;p>StateFlow 需要将初始状态传递给构造函数，而 LiveData 不需要。
当 View 进入 STOPPED 状态时，LiveData.observe() 会自动取消注册使用方，而从 StateFlow 或任何其他数据流收集数据的操作并不会自动停止。如需实现相同的行为，您需要从 Lifecycle.repeatOnLifecycle 块收集数据流。&lt;/p>
&lt;/blockquote>
&lt;p>这是&lt;a href="https://developer.android.com/kotlin/flow/stateflow-and-sharedflow?hl=zh-cn">Android Developer-StateFlow 和 SharedFlow&lt;/a>中写到的。&lt;/p>
&lt;p>的确LiveData构造方法中可以不传递值，为什么这么设计呢？我认为两者设计的用途是不同的。&lt;/p>
&lt;p>LiveData的不管是从名字还是实现上，真的很像是一个Data，内部有value，有版本，会让新增加的观察者保持最新的状态，也就是粘性事件（非要叫什么&amp;quot;数据倒灌&amp;quot;好显得高级- -）。一般会用需要传入LifeCycleOwn的方法注册观察者，因此生命周期是和LifeCycle绑定的，所以是live的。&lt;/p>
&lt;p>LiveData更新值时是需要在主线程执行的，上面的考虑在一起，它就是用来保持UI和一个数据的状态（粘性事件，最好不要执行逻辑）是同步的，因此是实现MVVM架构的一个关键部分。&lt;/p>
&lt;p>一个Data没有初始值，也很合理。&lt;/p>
&lt;p>那StateFlow呢？StateFlow首先是一个flow，在Kotlin中Flow最基本的作用是发送数据，StateFlow还是一个热流（观察者中只能收集到订阅之后的数据变化），不像冷流能接收到从最开始发送的数据。且是和生命周期没有关联的，有了协程的加持，使用的范围可以更大些，可以从其他子线程收集到之后发给任意线程，而不是仅限于主线程。&lt;/p>
&lt;p>State能没有初始值吗，听起来不太合适。&lt;/p>
&lt;p>StateFlow能替换LiveData吗？我认为的是可以的。&lt;/p>
&lt;p>LiveData两大特点分别是和生命周期绑定、粘性事件。生命周期StateFlow借助扩展函数repeatOnLifecycle可以做到，粘性事件也是本身就支持的。&lt;/p>
&lt;p>但是，LiveData不需要借助协程，开箱即用，有更具体的使用场景。&lt;/p></description></item><item><title>协程与Kotlin协程对比</title><link>https://opoojkk.com/posts/2025/06/22/coroutine-and-kotlin-coroutine/</link><pubDate>Sun, 22 Jun 2025 17:31:18 +0800</pubDate><guid>https://opoojkk.com/posts/2025/06/22/coroutine-and-kotlin-coroutine/</guid><description>&lt;p>协程到底是什么呢？&lt;/p>
&lt;p>这是我看到Kotlin协程之后的第一个问题。在回答是不是什么之前，先弄清楚什么是怎么定义的，协程也是一样。&lt;/p>
&lt;p>在搜索引擎得到的答案都是&amp;quot;轻量化线程&amp;quot;、&amp;ldquo;非阻塞&amp;rdquo;、&amp;ldquo;协作式&amp;quot;之类的关键字关键字，真的是这样吗？&lt;/p>
&lt;blockquote>
&lt;p>协程（英语：coroutine）是计算机程序的一类组件，推广了协作式多任务的子例程，允许执行被挂起与被恢复。相对子例程而言，协程更为一般和灵活，但在实践中使用没有子例程那样广泛。协程更适合于用来实现彼此熟悉的程序组件，如协作式多任务、异常处理、事件循环、迭代器、无限列表和管道。&lt;/p>
&lt;p>摘自wikipedia&lt;/p>
&lt;/blockquote>
&lt;p>维基百科的解释是这样的，看上去好像也不能理解是什么。&lt;/p>
&lt;p>维基百科的优点是会给出来源，协程最初被提出是在1963年。按照网络上的说法，线城市60年代后期出现了，按照这个说法，线程出现比协程要晚。因此，&amp;ldquo;轻量化线程&amp;quot;看起来不能成立。&lt;/p>
&lt;p>在原始的论文中，作者表述的是一种没有主程序控制，由多段不同的协程控制的程序，子程序之间没有父子关系都是并列的。&lt;/p>
&lt;p>但是被认为是协程里程碑的则是另一篇《Coroutines: A Programming Methodology, a Language Design and an Implementation》。&lt;/p>
&lt;p>其中表述的概念是说协程是非抢占式的。抢占式指操作系统强制终端当前的任务，将CPU分配给其他任务，而协程不一样，是通过主动让出CPU，这时候才会切换至其他任务。最常见的场景恐怕是IO操作上，当需要切换线程时，不是由操作系统完成，而是有程序员主动执行，让正在执行IO操作的线程让出CPU。&lt;/p>
&lt;p>协程是一个概念。无论是操作系统实现，还是语言实现，亦或是一个封装好的三方库，只要符合概念，通通都可以认为是协程。因此朱凯的理论&amp;quot;因为JVM没实现，所以不是真的协程&amp;quot;并不能让人信服。&lt;/p>
&lt;p>再到Kotlin的协程，Kotlin的协程实现了上面的概念了吗，我想是没有的，Kotlin的协程在使用的时候看起来是切换了线程，实际上是在主线程、IO线程池和耗时运算线程池之间切换，没有释放出正在IO等待的线程的CPU。&lt;/p>
&lt;p>从这个角度看，的确不是协程，但不妨碍它的名字叫Kotlin协程。&lt;/p>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%E5%8D%8F%E7%A8%8B">Wikipedia-协程&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://mp.weixin.qq.com/s/2q1dxT4QxsuxP3wmrUa4Ag">协程简史，一文讲清楚协程的起源、发展和实现&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://link.springer.com/content/pdf/bfm:978-3-540-38378-9/1">A Programming Methodology, a Language Design and an Implementation&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>WebView JavaScript接口实现原理</title><link>https://opoojkk.com/posts/2025/05/26/implementing-addjavascriptinterface-in-android-webview/</link><pubDate>Mon, 26 May 2025 09:13:11 +0800</pubDate><guid>https://opoojkk.com/posts/2025/05/26/implementing-addjavascriptinterface-in-android-webview/</guid><description>&lt;p>在工作中有和前端混合开发的项目（前端的代码运行在Webview中），混合开发涉及到前端和客户端之间方法的调用，花了点时间弄明白是实现调用的。&lt;/p>
&lt;h2 class="heading" id="实现">
 实现&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%9e%e7%8e%b0">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>我们都知道是通过WebView的addJavaScript方法将Android的方法添加进Webview，之后前端才可以调用到的，因此入口从它开始。&lt;/p>
&lt;h3 class="heading" id="java层">
 Java层&lt;span class="heading__anchor"> &lt;a href="#java%e5%b1%82">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>点进WebView的&lt;code>addJavascriptInterface&lt;/code>方法里，添加和移除的方法实现都是通过&lt;code>WebViewProvider&lt;/code>。&lt;code>WebViewProvider&lt;/code>才是WebView中功能实现的类。又是怎么创建的呢？&lt;/p>
&lt;p>WebViewProvider的创建通过反射方式。在不同Android SDK中指向的不同的FactoryProvider类。详细过程可以参考&lt;a href="https://juejin.cn/post/6844903922520293389">【迁移博客】WebView深究之Android是如何实现webview初始化的&lt;/a>。&lt;/p>
&lt;p>在我的环境下，&lt;code>WebViewProvider&lt;/code>实现类是&lt;code>WebViewChromium&lt;/code>。&lt;/p>
&lt;p>我们要找的&lt;code>addJavascriptInterface&lt;/code>又被给到了&lt;code>AwContents&lt;/code>了，&lt;code>AwContents&lt;/code>又给&lt;code>JavascriptInjector&lt;/code>&amp;hellip;&lt;/p>
&lt;p>终于，对应到了&lt;code>content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java&lt;/code>的&lt;code>addPossiblyUnsafeInterface&lt;/code>。&lt;/p>
&lt;p>&lt;code>addPossiblyUnsafeInterface&lt;/code>方法接收到的四个参数分别是：&lt;/p>
&lt;ol>
&lt;li>实现JS方法的对象&lt;/li>
&lt;li>addJavaScript时传入的name&lt;/li>
&lt;li>JavascriptInterface.class&lt;/li>
&lt;li>List.of(&amp;quot;*&amp;quot;)&lt;/li>
&lt;/ol>
&lt;p>观察到&lt;code>addPossiblyUnsafeInterface&lt;/code>的实现是一个需要传入指针的，下面的Natices接口标注了NativeMethods，而且搜到的几个&lt;code>JavascriptInjectorImplJni#get&lt;/code>方法路径都在out/&amp;hellip;路径下，猜测编译时生成的C++代码。&lt;/p>
&lt;p>或者，你也可以向我一样，用ChatGPT搜对应的实现是什么，再编一顿和上面类似的理由😄。&lt;/p>
&lt;p>根据规则，对应的C++实现类content/browser/android/javascript_injector_android.cc。&lt;/p>
&lt;h3 class="heading" id="native层">
 native层&lt;span class="heading__anchor"> &lt;a href="#native%e5%b1%82">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>从这里开始，就到了C++的部分。&lt;/p>
&lt;p>C++我不熟悉，也就是大致能看懂的水平，有看不懂的交给AI解释了的。&lt;/p>
&lt;p>下面是添加方法的实现，将Android传递过来的方法封装成一个C++对象，用一个map存放着。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="n">GinJavaBridgeDispatcherHost&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">AddNamedObject&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">JavaRef&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">jobject&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">object&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">JavaRef&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">jclass&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">safe_annotation_clazz&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">net&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">SchemeHostPortMatcher&lt;/span> &lt;span class="n">matcher&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DCHECK_CURRENTLY_ON&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BrowserThread&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">UI&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">GinJavaBoundObject&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ObjectID&lt;/span> &lt;span class="n">object_id&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">NamedObjectMap&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">iterator&lt;/span> &lt;span class="n">iter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">named_objects_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="n">existing_object&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FindObjectId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">object&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">existing_object&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">iter&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="n">named_objects_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">end&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">iter&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">second&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">object_id&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">object_id&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Nothing to do.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">iter&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="n">named_objects_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">end&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">RemoveNamedObject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">iter&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">first&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">existing_object&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">AutoLock&lt;/span> &lt;span class="n">locker&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">objects_lock_&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">objects_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">AddName&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">object_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AddObject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">object&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">safe_annotation_clazz&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">nullopt&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// We use the serialized string of the matcher and reconstruct it
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// in the render process. We pass this around like this because we can
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// then trust that all the rules being fed to the render process are well
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// formed rules.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// TODO(crbug.com/407420300): Rely on OriginMatcher instead of a string here.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">named_objects_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">matcher&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ToString&lt;/span>&lt;span class="p">()};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">web_contents&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetPrimaryMainFrame&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ForEachRenderFrameHostImplIncludingSpeculative&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">object_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">matcher&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">](&lt;/span>&lt;span class="n">RenderFrameHostImpl&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">render_frame_host&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">render_frame_host&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">IsRenderFrameLive&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">GetJavaBridge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">render_frame_host&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="cm">/*should_create=*/&lt;/span>&lt;span class="nb">true&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">AddNamedObject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">object_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">matcher&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ToString&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>既然存放在一个map里，调用时也一定是通过这个map取到的，顺着找到了&lt;code>content/browser/android/java/gin_java_bridge_dispatcher_host.cc#onInvokeMethod&lt;/code>，看到参数中使用方法名、参数和结果的，结合之前和前端打交道的经验，十有八九就是它了。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="n">GinJavaBridgeDispatcherHost&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">OnInvokeMethod&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">GlobalRenderFrameHostId&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">routing_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">GinJavaBoundObject&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ObjectID&lt;/span> &lt;span class="n">object_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">method_name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">arguments&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">wrapped_result&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">mojom&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GinJavaBridgeError&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">error_code&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DCHECK&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">JavaBridgeThread&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CurrentlyOn&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DCHECK&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">routing_id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scoped_refptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">GinJavaBoundObject&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">object&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FindObject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">wrapped_result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">Append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span>&lt;span class="n">error_code&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mojom&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GinJavaBridgeError&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">kGinJavaBridgeUnknownObjectId&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MakeRefCounted&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">GinJavaMethodInvocationHelper&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_unique&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">GinJavaBoundObjectDelegate&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">object&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">method_name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">arguments&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">Init&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">Invoke&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span>&lt;span class="n">error_code&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetInvocationError&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">HoldsPrimitiveResult&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span>&lt;span class="n">wrapped_result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetPrimitiveResult&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">Clone&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="nf">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetObjectResult&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">is_null&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">GinJavaBoundObject&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ObjectID&lt;/span> &lt;span class="n">returned_object_id&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">FindObjectId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetObjectResult&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">returned_object_id&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">AutoLock&lt;/span> &lt;span class="n">locker&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">objects_lock_&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">objects_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">returned_object_id&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">AddHolder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">routing_id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">returned_object_id&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AddObject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetObjectResult&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetSafeAnnotationClass&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">routing_id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">wrapped_result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">Append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">FromUniquePtrValue&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">GinJavaBridgeValue&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateObjectIDValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">returned_object_id&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">wrapped_result&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">Append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>接着是调用Java方法&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="n">GinJavaMethodInvocationHelper&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Invoke&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">JNIEnv&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">env&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AttachCurrentThread&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">JavaMethod&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">method&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">object_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">FindMethod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">method_name_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">arguments_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">method&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SetInvocationError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mojom&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GinJavaBridgeError&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">kGinJavaBridgeMethodNotFound&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">object_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">IsObjectGetClassMethod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">method&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">EventLogWriteInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">kObjectGetClassInvocationAttemptLogTag&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">getuid&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SetInvocationError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mojom&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GinJavaBridgeError&lt;/span>&lt;span class="o">::&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">kGinJavaBridgeAccessToObjectGetClassIsBlocked&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ScopedJavaLocalRef&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">jobject&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">obj&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ScopedJavaLocalRef&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">jclass&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">cls&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">method&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">is_static&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cls&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">object_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetLocalClassRef&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">obj&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">object_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetLocalRef&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">obj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">is_null&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">cls&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">is_null&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SetInvocationError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mojom&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GinJavaBridgeError&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">kGinJavaBridgeObjectIsGone&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mojom&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GinJavaBridgeError&lt;/span> &lt;span class="n">coercion_error&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mojom&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GinJavaBridgeError&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">kGinJavaBridgeNoError&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">jvalue&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">parameters&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">method&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">num_parameters&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">method&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">num_parameters&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">argument&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">arguments_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">parameters&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">CoerceJavaScriptValueToJavaValue&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">env&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">argument&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">method&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">parameter_type&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">object_refs_&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">coercion_error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">coercion_error&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">mojom&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GinJavaBridgeError&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">kGinJavaBridgeNoError&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">method&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">is_static&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">InvokeMethod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">nullptr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cls&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">obj&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">method&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">return_type&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">method&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">parameters&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">InvokeMethod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">obj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">obj&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">method&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">return_type&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">method&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">parameters&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SetInvocationError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">coercion_error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Now that we&amp;#39;re done with the jvalue, release any local references created
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// by CoerceJavaScriptValueToJavaValue().
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">method&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">num_parameters&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ReleaseJavaValueIfRequired&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">parameters&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">method&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">parameter_type&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>现在重新梳理一下完成的流程，Android中为一个WebView添加JS方法时，会通过反射创建不同的WebViewProvider实现，用的最多的是WebViewChromium。添加的JS方法会以一个对象的形式，存储在CPP的一个map中，调用时通过name和方法名称找到添加的方法。&lt;/p>
&lt;p>Android和JS通信要借助Native层完成。&lt;/p>
&lt;h2 class="heading" id="工具">
 工具&lt;span class="heading__anchor"> &lt;a href="#%e5%b7%a5%e5%85%b7">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Chromium代码搜索：&lt;a href="https://source.chromium.org/">Chromium Code Search&lt;/a>&lt;/p>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://juejin.cn/post/6844903922520293389">【迁移博客】WebView深究之Android是如何实现webview初始化的&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>理解Android插件化</title><link>https://opoojkk.com/posts/2025/05/11/android-plugin/</link><pubDate>Sun, 11 May 2025 14:23:04 +0800</pubDate><guid>https://opoojkk.com/posts/2025/05/11/android-plugin/</guid><description>&lt;p>12年我还在上初中，AndroidDynamicLoader已经只吃了Fragment的插件化实现。之后不断演变，从dynamic-load-apk，到DroidPlugin，再到Atlas。&lt;/p>
&lt;p>说来惭愧，时至今日才有机会接触到，不禁想一探究竟。&lt;/p>
&lt;h2 class="heading" id="起源">
 起源&lt;span class="heading__anchor"> &lt;a href="#%e8%b5%b7%e6%ba%90">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在想一项技术被怎么实现、怎么应用前，总是有这样的疑问：为什么要有这项技术呢？插件化也是一样的，为什么一定要用插件化呢？&lt;/p>
&lt;p>因为是25年才搜索，并没有真正的经历过插件化的演变，查资料得知：&lt;/p>
&lt;ol>
&lt;li>解决Dex中方法数上限65536的问题；&lt;/li>
&lt;li>大项目需求迭代，提高多个模块并行开发效率，减小应用体积，同时还能动态化修改功能（主模块之外）。&lt;/li>
&lt;/ol>
&lt;p>对第一个问题，&lt;a href="https://developer.android.com/build/multidex">Enable multidex for apps with over 64K methods&lt;/a>Android开发官方的文档中说明，从Android5.0开始，已经支持了MultiDex，用以解决65536上限的问题。&lt;/p>
&lt;p>但是，考虑到应用适配的速度，尤其是一几年，没有升级到SDK版本还停留在Android 5.0之前是很正常的。虽然升级的成本要比插件化低很多😅。&lt;/p>
&lt;blockquote>
&lt;p>插一句：Android官方提供用在Android5.0之前解决多DEX方法数上限的库中实际上也是把APK解压，通过classloader加载dex文件。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Android 5.0（API 级别 21）及更高版本使用名为 ART 的运行时，它本身支持从 APK 文件加载多个 DEX 文件。
——&lt;a href="https://developer.android.com/build/multidex?hl=zh-cn#mdex-on-l">为方法数超过 64K 的应用启用 MultiDex&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>第二个问题使用其他方式可以解决吗？至少可以解决一部分，开发效率可以考虑组件化的方式。减小应用体积动态下载这部分是不可以的。&lt;/p>
&lt;p>因此，动态化是有必要的。&lt;/p>
&lt;h2 class="heading" id="需要解决的问题">
 需要解决的问题&lt;span class="heading__anchor"> &lt;a href="#%e9%9c%80%e8%a6%81%e8%a7%a3%e5%86%b3%e7%9a%84%e9%97%ae%e9%a2%98">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>插件化即是动态下载好要执行的代码，在Android应用中涉及到三方面：&lt;/p>
&lt;ol>
&lt;li>逻辑代码&lt;/li>
&lt;li>四大组件&lt;/li>
&lt;li>资源文件&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="实现方式">
 实现方式&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%9e%e7%8e%b0%e6%96%b9%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="逻辑代码">
 逻辑代码&lt;span class="heading__anchor"> &lt;a href="#%e9%80%bb%e8%be%91%e4%bb%a3%e7%a0%81">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>逻辑代码只要能被类加载器加载过，找到对应的路径就是可以执行的。&lt;/p>
&lt;p>在Android中有两个classloader被用来加载dex文件：PathClassLoader和DexClassLoader。两者在Android8.0之后实际上也没有差异了。&lt;/p>
&lt;h3 class="heading" id="四大组件">
 四大组件&lt;span class="heading__anchor"> &lt;a href="#%e5%9b%9b%e5%a4%a7%e7%bb%84%e4%bb%b6">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>四大组件也是类，为什么不和其他逻辑代码放到一起呢？&lt;/p>
&lt;p>因为在Android中启动一个四个组件的类是会鉴别是否在清单文件中的，如果不在其中，会被拒绝启动。&lt;/p>
&lt;p>解决的思路有两种：&lt;/p>
&lt;ol>
&lt;li>清单文件中预埋容器Activity，容器中声明周期方法被调用时，手动调用实际Activity的；&lt;/li>
&lt;li>利用hook，将插件 Activity 替换成实际Activity，等到检测完了后再换回来。&lt;/li>
&lt;/ol>
&lt;h3 class="heading" id="资源文件">
 资源文件&lt;span class="heading__anchor"> &lt;a href="#%e8%b5%84%e6%ba%90%e6%96%87%e4%bb%b6">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>当我们访问资源文件时，比如string。实际上是从ResourceImpl中的AssetsManager中返回的。&lt;/p>
&lt;p>向ResourceImpl中添加的方法是assets.addAssetPath，所以我们想将插件的apk文件添加到宿主中，就可以通过反射修改这个地方。&lt;/p>
&lt;p>实现步骤：&lt;/p>
&lt;ol>
&lt;li>创建一个AssetManager对象，并调用 addAssetPath 方法，将插件 apk 的路径作为参数传入。&lt;/li>
&lt;li>将第一步创建的AssetManager对象作为参数，创建一个新的Resources对象，并返回给插件使用。&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://juejin.cn/post/6973888932572315678">浅谈Android插件化&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.cn/post/6844903929562529800">谈谈 Android 中的 PathClassLoader 和 DexClassLoader&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/434090037">深入源码分析Android插件化原理&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Android序列化方案对比</title><link>https://opoojkk.com/posts/2025/03/23/serializable-and-parcelable-on-android/</link><pubDate>Sun, 23 Mar 2025 16:23:42 +0800</pubDate><guid>https://opoojkk.com/posts/2025/03/23/serializable-and-parcelable-on-android/</guid><description>&lt;p>从开始学习Android，到现在看过很多次有人争论Android为什么用Parcelable，而不是Java提供的Serializable接口。&lt;/p>
&lt;p>除了使用两者的方式不同外，国内（大陆）目前最普遍的观点是认为Serializable用了大量的反射，导致速度要比Parcelable慢。&lt;/p>
&lt;p>但，真的是这样吗？&lt;/p>
&lt;p>有许多人提出，是因为用到Serializable时，开发者并没有定义应该怎么序列化和反序列化，因此Java为你用反射完成的。实际上是可以实现Custom Serialization的。并没有提供给方法重写，如果想要这么做，你需要实现private的writeObject和readObject方法。你可以参考&lt;a href="https://www.oracle.com/technical-resources/articles/java/serializationapi.html">Discover the secrets of the Java Serialization API&lt;/a>实现。&lt;/p>
&lt;p>用这样的Serializable和Parcelable比较未免太不公平了。实际上，如果实现了Custom Serialization，它是要比Parcelable的写快大概3.6倍，比Parcelable的读取要快上1.6倍的。（速度未验证）。按照这个逻辑，Android中用Parcelable是有其他的原因。&lt;/p>
&lt;p>StackOverFlow上的一个大牛解释，Parcelable除了可以实现进程间通信之外，还可以实现native和Java层的通信（Java层序列化后从native层读取出来）。&lt;/p>
&lt;p>参考:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://stackoverflow.com/a/22350463/22727751">Parcelable is sort of a standard in Android development. But not because of speed&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://android.googlesource.com/platform/frameworks/native/+/brillo-m10-dev/include/binder/Parcelable.h">Parcelable.h&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://stackoverflow.com/a/58875329/22727751">Custom Class to be serialized&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://www.oracle.com/technical-resources/articles/java/serializationapi.html">Discover the secrets of the Java Serialization API&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://www.geeksforgeeks.org/customized-serialization-and-deserialization-in-java/">Customized Serialization and Deserialization In Java&lt;/a>&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Mac终端中使用VS Code打开文件</title><link>https://opoojkk.com/posts/2025/03/08/open-file-with-vscode/</link><pubDate>Sat, 08 Mar 2025 09:35:43 +0800</pubDate><guid>https://opoojkk.com/posts/2025/03/08/open-file-with-vscode/</guid><description>&lt;p>最近排查ANR日志，用grep找到包含特定内容的日志后，还要找文件在哪再打开，觉得很繁琐，简单搜了下，还有平时常用的命令，一并记录下来以便后续用到查找。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 用特定软件打开某个文件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">open -a &lt;span class="s2">&amp;#34;软件名称&amp;#34;&lt;/span> path/file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># finder中打开当前文件夹&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">open .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># grep递归匹配特定内容&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">grep -r &lt;span class="s2">&amp;#34;特定内容&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># grep递归匹配特定内容，只显示文件名&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">grep -rl &lt;span class="s2">&amp;#34;特定内容&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>DialogFragment取消机制解析</title><link>https://opoojkk.com/posts/2025/01/19/setcanceledontouchoutside-does-not-work-in-dialogfragment/</link><pubDate>Sun, 19 Jan 2025 11:18:47 +0800</pubDate><guid>https://opoojkk.com/posts/2025/01/19/setcanceledontouchoutside-does-not-work-in-dialogfragment/</guid><description>&lt;p>某天（周六）测试反馈一个本该不能用返回取消的弹窗，是可以用返回手势取消的。心中满是疑惑，代码中已经写了&lt;code>dialog?.setCanceledOnTouchOutside(false)&lt;/code>怎么不生效呢？&lt;/p>
&lt;p>带着关键字查Google，都说用&lt;code>setCancelable(false)&lt;/code>就可以了，大为震惊。日思夜想睡不着，今天一定要查个明白。&lt;/p>
&lt;h2 class="heading" id="dialog">
 Dialog&lt;span class="heading__anchor"> &lt;a href="#dialog">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Dialog的方法做了是什么?&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Dialog.java
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Sets whether this dialog is canceled when touched outside the window&amp;#39;s
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * bounds. If setting to true, the dialog is set to be cancelable if not
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * already set.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param cancel Whether the dialog should be canceled when touched outside
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * the window.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="n">void&lt;/span> &lt;span class="n">setCanceledOnTouchOutside&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">boolean&lt;/span> &lt;span class="n">cancel&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">cancel&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="n">mCancelable&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mCancelable&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mWindow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">setCloseOnTouchOutside&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cancel&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Window.java
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/** @hide */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@UnsupportedAppUsage&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="n">void&lt;/span> &lt;span class="n">setCloseOnTouchOutside&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">boolean&lt;/span> &lt;span class="n">close&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mCloseOnTouchOutside&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">close&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mSetCloseOnTouchOutside&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>两件事：&lt;/p>
&lt;ol>
&lt;li>如果cancel的值为true，保证mCancelable为true。&lt;/li>
&lt;li>改变Window的值。&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="dialogfragment">
 DialogFragment&lt;span class="heading__anchor"> &lt;a href="#dialogfragment">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>为什么是DialogFragment而不是Dialog，是DialogFragment是可以用Fragment的生命周期的，Dialog作为Fragment中的一个成员变量。&lt;/p>
&lt;p>Dialog是在DialogFragment创建View的时候，更确切的说是在获取LayoutInflater的时候创建了Dialog实例。DialogFragment的构造方法中穿进去的layoutId也是用作创建dialog，并没有真的把这个layout加载出来显示在Fragment中。&lt;/p>
&lt;p>因此更改dialog的属性至少应该在onCreateView之后，&lt;/p>
&lt;p>&lt;code>setCancelable&lt;/code>做了什么？&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Called when the dialog has detected the user&amp;#39;s press of the back
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * key. The default implementation simply cancels the dialog (only if
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * it is cancelable), but you can override this to do whatever you want.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * If you target version {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, you
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * should not use this method but register an {@link OnBackInvokedCallback} on an
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@link OnBackInvokedDispatcher} that you can retrieve using
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@link #getOnBackInvokedDispatcher()}. You should also set
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@code android:enableOnBackInvokedCallback=&amp;#34;true&amp;#34;} in the application manifest.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;p&amp;gt;Alternatively, you
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * can use {@code androidx.activity.ComponentDialog#getOnBackPressedDispatcher()}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * for backward compatibility.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @deprecated Use {@link OnBackInvokedCallback} or
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@code androidx.activity.OnBackPressedCallback} to handle back navigation instead.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Starting from Android 13 (API level 33), back event handling is
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * moving to an ahead-of-time model and {@link #onBackPressed()} and
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@link KeyEvent#KEYCODE_BACK} should not be used to handle back events (back gesture or
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * back button click). Instead, an {@link OnBackInvokedCallback} should be registered using
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@link Dialog#getOnBackInvokedDispatcher()}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@link OnBackInvokedDispatcher#registerOnBackInvokedCallback(int, OnBackInvokedCallback)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * .registerOnBackInvokedCallback(priority, callback)}.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Deprecated&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">onBackPressed&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mCancelable&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">cancel&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="取消的问题">
 取消的问题&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%96%e6%b6%88%e7%9a%84%e9%97%ae%e9%a2%98">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>我们知道事件（MotionEvent）先发送至ViewRootImpl，然后给对应的Window。在Dialog中的Window同样对应PhoneWIndow实例。随后Window先把事件传给Window.Callback，Activity和Dialog都实现了Window.Callback接口，所以它们才能收到事件。&lt;/p>
&lt;p>Window.Callback先把事件分发给自己的View，从DecorView开始，一直到根节点的View。如果所有View都不消费事件，事件最后还是回到Window.Callback，在这里是Dialog。交给onTouchEvent。可以看到是判断能不能被取消且有没有点击在Window上，如果都满足，Dialog就被cancel了。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Called when a touch screen event was not handled by any of the views
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * under it. This is most useful to process touch events that happen outside
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * of your window bounds, where there is no view to receive it.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param event The touch screen event being processed.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @return Return true if you have consumed the event, false if you haven&amp;#39;t.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * The default implementation will cancel the dialog when a touch
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * happens outside of the window bounds.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="n">boolean&lt;/span> &lt;span class="n">onTouchEvent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@NonNull&lt;/span> &lt;span class="n">MotionEvent&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">mCancelable&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">mShowing&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">mWindow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">shouldCloseOnTouch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mContext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cancel&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>mWindow.shouldCloseOnTouch&lt;/code>中判断点击在Window外部需要不需要取消的依据就是&lt;code>mCloseOnTouchOutside&lt;/code>。&lt;/p>
&lt;p>到这里&lt;code>setCanceledOnTouchOutside&lt;/code>这一部分清晰了。&lt;/p>
&lt;p>在Android中除了MotionEvent还有另一套事件——KeyEvent，用作分发按键。&lt;/p>
&lt;p>KeyEvent事件会先交给DecorView，DecorView又把事件给了Window.Callback&amp;hellip;.是不是很熟悉，没错，又到了DIalog。只不过这次是dispatchKeyEvent。如果没有消费，最后依旧回到Dialog。如果事件手指离开按键，那么会触发&lt;code>onBackPressed()&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">onBackPressed&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mCancelable&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">cancel&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>2024：我的成长与思考</title><link>https://opoojkk.com/posts/2025/01/12/2024/</link><pubDate>Sun, 12 Jan 2025 13:32:33 +0800</pubDate><guid>https://opoojkk.com/posts/2025/01/12/2024/</guid><description>&lt;p>我没有忘写年终总结，但是忘了去年写在哪，今天才找到。按照惯例总结下去年在各方面，写下新一年的目标。&lt;/p>
&lt;h2 class="heading" id="工作">
 工作&lt;span class="heading__anchor"> &lt;a href="#%e5%b7%a5%e4%bd%9c">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>23年11月份入职，到元旦一年出头，时间算不上长。与之前相比，感受最明显的还是流程，在上一家需求评审完了就可以开始干活了，在这里才刚开始。到真的开始写需求，需要经过预沟通（和领导们，没我什么事）、需求评审、技术预审、技术内审、技术外审后才是真的开始写代码。这背后的是对风险的把控，时间都是算好的，每一个齿轮都要按照争取的节奏转动。&lt;/p>
&lt;p>收获更多的是作为一个owner（不喜欢这个词），背上责任，压力倍增，附带效果是人稍自信了些，代价是交出了自己的时间。&lt;/p>
&lt;h2 class="heading" id="生活">
 生活&lt;span class="heading__anchor"> &lt;a href="#%e7%94%9f%e6%b4%bb">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>这一年去了成都，从成都去了重庆，中间去了大同，十月份又去了香港，和上一次的香港有很大不同。在年前去了南京，又从南京跑到杭州，赶在远到前回到北京。&lt;/p>
&lt;p>更喜欢旅行，喜欢看形形色色的人，不同城市的人是怎么生活的，有什么不同的文化，很有趣。
相机又买了索尼，买东西像是做选择题，没有办法做到尽善尽美。看中优势，排除劣势或者接受没有缺点。&lt;/p>
&lt;p>突然意识到26周岁了。&lt;/p>
&lt;p>朋友还是几个，工作中认识了更多几个，认识人很容易，相处得来不是件容易事。&lt;/p>
&lt;h2 class="heading" id="读书">
 读书&lt;span class="heading__anchor"> &lt;a href="#%e8%af%bb%e4%b9%a6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>这一年看了120个小时，每天大概19分钟，甚至比上一年更少，总有各种各样的新奇事想尝试，读书上花费的时间少的可怜。&lt;/p>
&lt;p>今年印象最深刻的几本书：&lt;/p>
&lt;ol>
&lt;li>《看见》 - 看见人。&lt;/li>
&lt;li>《李光耀观天下（精装版）》 - 李光耀视角下的东南亚国家和美国。&lt;/li>
&lt;li>《丰乳肥臀》 - 社会的变迁和伟大的母亲。&lt;/li>
&lt;li>《哲学家们都干了些什么（修订版）》 - 哲学史也是找寻人生意义的历程。&lt;/li>
&lt;li>《无人生还》 - 阿加莎精彩的悬疑小说。&lt;/li>
&lt;li>《王能好》 - 乡村人王能好。&lt;/li>
&lt;li>《新摄影笔记》 - 补足摄影技巧。&lt;/li>
&lt;/ol>
&lt;p>如果只能推荐一本，我会选《哲学家们都干了些什么（修订版）》。如果你对哲学感兴趣，又恰好想知道人生的意义到底是什么，很是推荐。&lt;/p>
&lt;h2 class="heading" id="2025新年目标">
 2025新年目标&lt;span class="heading__anchor"> &lt;a href="#2025%e6%96%b0%e5%b9%b4%e7%9b%ae%e6%a0%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>去年的目标几乎都完成了，结果还不错。写下目标的意义是提醒自己有几个方向需要努力。&lt;/p>
&lt;ol>
&lt;li>㊙️&lt;/li>
&lt;li>读书时间150小时，日均阅读时长20+分钟。（和去年一样）。&lt;/li>
&lt;li>养猫咪&lt;/li>
&lt;li>攒钱。&lt;/li>
&lt;li>能熟练用CPP。&lt;/li>
&lt;li>考一次雅思。&lt;/li>
&lt;li>再掌握一门新的技能。&lt;/li>
&lt;/ol></description></item><item><title>Context是什么</title><link>https://opoojkk.com/posts/2025/01/05/what-is-context/</link><pubDate>Sun, 05 Jan 2025 14:41:24 +0800</pubDate><guid>https://opoojkk.com/posts/2025/01/05/what-is-context/</guid><description>&lt;p>Context是什么？这是从我开始学习Android就有的疑问，刚入行时找了无数的文章，也没有谁能解答我的疑问，有的是不断的告诉我Context是上下文，这不废话吗，我查不到Context的意思是上下文吗？我的意思是什么的上下文。&lt;/p>
&lt;p>一转眼工作好几年了，实在惭愧。这两天突然想到Context（思维涣散，容易乱想），想到了Context。我想，现在我可以回答Context是什么了。&lt;/p>
&lt;p>还是拿出那张Context的族谱。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/ec3tY8z.jpg" alt="Context族谱">&lt;/figure>&lt;p>当你用到一个方法的时候，传的都是Context，很少是Activity或者是Application，我知道也有，但是少。&lt;/p>
&lt;p>Context还真就是上下文（也是废话），当你需要拿到资源的时候，真正给你资源的是ContextImpl对象，但是每个Context最后都会和ContextImpl联系起来。&lt;/p>
&lt;p>举个例子，当你用Context.getResources想通过这个拿到一个字符串或者一个图片的时候，context不管是哪种都会给你，但是只有ContextImpl有这个能力。这是因为当你用ContextWrapper中的一种时，ContextWrapper中持有ContextImpl的成员变量，会把从ContextImpl那拿到的给你（多像领导🤣），让你以为是ContextWrapper给你的。当你用的是ContextImpl时，给资源的就是它，当然也是可以的（一一般不会这么用）。&lt;/p>
&lt;p>只要你有Context，你就能拿到你想要的资源，这像不像是只要你站在某个区域，你就可以做到，这个是我理解的的上下文（Context）。&lt;/p>
&lt;p>其他的像是PackageInfo、ContentResolver、Theme&amp;hellip;都是类似的，外观模式。&lt;/p></description></item><item><title>Android代码混淆</title><link>https://opoojkk.com/posts/2025/01/05/android-proguard/</link><pubDate>Sun, 05 Jan 2025 10:41:55 +0800</pubDate><guid>https://opoojkk.com/posts/2025/01/05/android-proguard/</guid><description>&lt;p>在Android应用开发完成，上架前，一般是要经过加固和混淆两个步骤的，加固的原理我不懂，整理了混淆相关的知识和文档。供自己和有需要的朋友查阅。&lt;/p>
&lt;h2 class="heading" id="目的">
 目的&lt;span class="heading__anchor"> &lt;a href="#%e7%9b%ae%e7%9a%84">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>一定是有什么作用，否则是不会加入的。混淆的作用是什么呢？&lt;/p>
&lt;ol>
&lt;li>移除运行时不是用的方法和资源，减少应用体积（&lt;a href="https://developer.android.com/build/shrink-code?hl=zh-cn#shrink-code">缩减代码-官方介绍&lt;/a>）&lt;/li>
&lt;li>用更短的无意义的名称替换类名、属性名，替换后的代码更不宜读，增加反编译难度&lt;/li>
&lt;li>检查并重写代码，进一步减少应用dex大小，提高运行速度（&lt;a href="https://developer.android.com/build/shrink-code?hl=zh-cn#optimization">代码优化-官方介绍&lt;/a>）&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="配置">
 配置&lt;span class="heading__anchor"> &lt;a href="#%e9%85%8d%e7%bd%ae">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>我们知道Gradle是一套构建的工具，提供标准的流程，你可以用Gradle构建Java后端项目，可以构建Android项目。一般来说包括，Gradle需要经过初始化、配置、执行三个步骤。task是Gradle执行的最小单位，混淆task并不是流程中的一环，想要用Gradle混淆，是需要手动添加混淆task的。&lt;/p>
&lt;p>Android项目之所以不需要你手动添加混淆的插件，简单的几行代码是开启混淆，是在Android项目的Gradle插件android中已经帮你做了这部分工作。你只需要修改几个配置即可。Android是用的是Google推出的R8，使用ProGuard 规则文件来修改其默认行为。&lt;/p>
&lt;h3 class="heading" id="开启混淆">
 开启混淆&lt;span class="heading__anchor"> &lt;a href="#%e5%bc%80%e5%90%af%e6%b7%b7%e6%b7%86">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>你需要在开启混淆的Android模块的build.gradle文件中，设置minifyEnabled的值为true，比如最常用的在构建release时：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">android {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> buildTypes {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> release {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // debuggable置为false是开启混淆的前提
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> debuggable false
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> minifyEnabled true
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // 缩减没有用到的资源
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> shrinkResources true
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // 混淆规则配置
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> proguardFiles getDefaultProguardFile(
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#39;proguard-android-optimize.txt&amp;#39;),
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#39;proguard-rules.pro&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当你新建一个Android项目时，模板会帮你添加上proguardfiles这行代码，即使你不开启混淆。&lt;/p>
&lt;p>虽然写在一行，其实是两个文件，你可以把多个配置列在这里，中间用英文逗号隔开。再说模板加上的两个文件含义，&lt;code>proguard-android-optimize.txt&lt;/code>从Android Gradle插件生成的，其中包含了对大多数Android项目都能用的规则，当你用Android Studio创建新模块，会帮你创建&lt;code>proguard-rules.pro&lt;/code>文件，默认是空的，需要你添加规则。此外还包含其他配置文件。&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>来源&lt;/th>
 &lt;th>位置&lt;/th>
 &lt;th>说明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Android Studio&lt;/td>
 &lt;td>&lt;code>&amp;amp;lt;module-dir&amp;amp;gt;/&amp;lt;wbr/&amp;gt;proguard-rules.&amp;lt;wbr/&amp;gt;pro&lt;/code>&lt;/td>
 &lt;td>当您使用 Android Studio 创建新模块时，Android Studio 会在该模块的根目录中创建 &lt;code>proguard-rules.&amp;lt;wbr/&amp;gt;pro&lt;/code> 文件。&lt;br />默认情况下，此文件不会应用任何规则。因此，请在此处添加您自己的 ProGuard 规则，比如&lt;a href="https://developer.android.com/studio/build/shrink-code?hl=zh-cn#keep-code">自定义保留规则&lt;/a>。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Android Gradle 插件&lt;/td>
 &lt;td>由 Android Gradle 插件在编译时生成。&lt;/td>
 &lt;td>Android Gradle 插件会生成 &lt;code>proguard-android-optimize.&amp;lt;wbr/&amp;gt;txt&lt;/code>（其中包含了对大多数 Android 项目都有用的规则），并启用 &lt;a href="https://developer.android.com/reference/androidx/annotation/Keep?hl=zh-cn">&lt;code>@Keep\*&lt;/code>&lt;/a>​&lt;a href="https://developer.android.com/reference/androidx/annotation/Keep?hl=zh-cn"> 注解&lt;/a>。&lt;br />默认情况下，使用 Android Studio 创建新模块时，模块级 build 脚本会将此规则文件纳入到您的发布 build 中。&lt;br />&lt;strong>注意&lt;/strong>：虽然 Android Gradle 插件包含额外的预定义 ProGuard 规则文件，但建议您使用 &lt;code>proguard-android-optimize.txt&lt;/code>。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>库依赖项&lt;/td>
 &lt;td>在 AAR 库中：&lt;br /> &lt;code>proguard.txt&lt;/code>&lt;br />在 JAR 库中：&lt;br /> &lt;code>META-INF/proguard/&amp;lt;ProGuard-rules-file&amp;gt;&lt;/code>&lt;br />除了这些位置之外，Android Gradle 插件 3.6 或更高版本还支持&lt;a href="https://developer.android.com/build/shrink-code?hl=zh-cn#targeted-shrink-rules">有针对性缩减规则&lt;/a>。&lt;/td>
 &lt;td>如果某个 AAR 或 JAR 库是使用它自己的规则文件发布的，并且您将该库作为编译时依赖项纳入到项目中，那么 R8 在编译项目时会自动应用这些规则。&lt;br />除了传统的 ProGuard 规则之外，Android Gradle 插件 3.6 或更高版本还支持&lt;a href="https://developer.android.com/build/shrink-code?hl=zh-cn#targeted-shrink-rules">有针对性缩减规则&lt;/a>。这些规则针对特定缩减器（R8 或 ProGuard）以及特定缩减器版本。&lt;br />如果库需要某些规则才能正常运行，那么使用该库随附的规则文件将非常有用。也就是说，库开发者已经为您执行了问题排查步骤。&lt;br />不过，您应该知道，因为这些规则是累加的，所以库依赖项包含的某些规则无法移除，并且可能会影响应用其他部分的编译。例如，如果某个库包含停用代码优化的规则，该规则将针对整个项目停用优化。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Android 资源打包工具 2 (AAPT2)&lt;/td>
 &lt;td>使用 &lt;code>minifyEnabled true&lt;/code> 构建项目后：&lt;code>&amp;amp;lt;module-dir&amp;amp;gt;/&amp;lt;wbr/&amp;gt;build/&amp;lt;wbr/&amp;gt;intermediates/&amp;lt;wbr/&amp;gt;aapt_&amp;lt;wbr/&amp;gt;proguard_&amp;lt;wbr/&amp;gt;file/&amp;lt;wbr/&amp;gt;.&amp;lt;wbr/&amp;gt;.&amp;lt;wbr/&amp;gt;.&amp;lt;wbr/&amp;gt;/&amp;lt;wbr/&amp;gt;aapt_&amp;lt;wbr/&amp;gt;rules.&amp;lt;wbr/&amp;gt;txt&lt;/code>&lt;/td>
 &lt;td>AAPT2 会根据对应用清单中的类、布局及其他应用资源的引用，生成保留规则。例如，AAPT2 会为您在应用清单中注册为入口点的每个 activity 添加一个保留规则。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>自定义配置文件&lt;/td>
 &lt;td>默认情况下，当您使用 Android Studio 创建新模块时，IDE 会创建 &lt;code>&amp;amp;lt;module-dir&amp;amp;gt;/&amp;lt;wbr/&amp;gt;proguard-rules.&amp;lt;wbr/&amp;gt;pro&lt;/code>，以便您添加自己的规则。&lt;/td>
 &lt;td>您可以&lt;a href="https://developer.android.com/build/shrink-code?hl=zh-cn#add-configuration">添加其他配置&lt;/a>，R8 会在编译时应用这些配置。&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>你可以在模块的pooguard-rules.pro文件中添加&lt;code>-printconfiguration ~/tmp/full-r8-config.txt&lt;/code>以打印出所有规则。当然你可以修改路径。&lt;/p>
&lt;h2 class="heading" id="规则">
 规则&lt;span class="heading__anchor"> &lt;a href="#%e8%a7%84%e5%88%99">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>混淆规则不指定时，认为所有的类、成员变量、方法都可以缩减成a.b.c的形式，代码还在，知识名称变了。你需要指定保留哪些让你的应用能够用正常运行，（保留的越少越好，我们的目就是要把代码变成看不懂的形式）。&lt;/p>
&lt;p>规则实在是多，详细的参见&lt;a href="https://www.guardsquare.com/manual/configuration/usage">Configuration - Usage&lt;/a>&lt;/p>
&lt;h2 class="heading" id="还原">
 还原&lt;span class="heading__anchor"> &lt;a href="#%e8%bf%98%e5%8e%9f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Android SDK中提供了恢复还原的工具：retrace 。路径位于SDK包的：cmdline-tools/version/bin/中。当你的应用出现崩溃时，你可以用它找到混淆混淆前的路径。&lt;/p>
&lt;p>用法：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">retrace path-to-mapping-file &lt;span class="o">[&lt;/span>path-to-stack-trace-file&lt;span class="o">]&lt;/span> &lt;span class="o">[&lt;/span>options&lt;span class="o">]&lt;/span> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>之后终端会等你收入异常的堆栈，粘贴进去。终止：&lt;/p>
&lt;ul>
&lt;li>Linux、macOS：Control+D&lt;/li>
&lt;li>Windows：Ctrl+Z+Enter&lt;/li>
&lt;/ul>
&lt;p>即可看到原始堆栈信息。&lt;/p>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://developer.android.com/build/shrink-code?hl=zh-cn">缩减、混淆处理和优化应用&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://www.guardsquare.com/manual/configuration/usage">Configuration - Usage&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://developer.android.com/tools/retrace?hl=zh-cn">R8 retrace&lt;/a>&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>理解View焦点机制</title><link>https://opoojkk.com/posts/2025/01/04/view-focus/</link><pubDate>Sat, 04 Jan 2025 13:35:24 +0800</pubDate><guid>https://opoojkk.com/posts/2025/01/04/view-focus/</guid><description>&lt;p>本篇内容探讨View的焦点。怎么算有焦点，同一时间有几个View有焦点，焦点的分发又是怎么回事。&lt;/p>
&lt;h2 class="heading" id="焦点">
 焦点&lt;span class="heading__anchor"> &lt;a href="#%e7%84%a6%e7%82%b9">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>View有个方法叫hasFocus，返回值是boolean类型的，用来判断View是否有焦点。hasFocus只做了简单的一件事，判断flag中是否包含PFLAG_FOCUSED。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mPrivateFlags&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PFLAG_FOCUSED&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>因此只要有PFLAG_FOCUSED这个flag就是有焦点了。&lt;/p>
&lt;h2 class="heading" id="焦点分发">
 焦点分发&lt;span class="heading__anchor"> &lt;a href="#%e7%84%a6%e7%82%b9%e5%88%86%e5%8f%91">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>以PFLAG_FOCUSED作为突破点，很容易找到开端。最最开始是从ViewRootImpl开始的。事件的分发相信你是熟悉的，上层事件分发的起点也是焦点分发的起点，原因是在事件的ACTION_DOWN事件中，把焦点交给符合条件的View。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">performFocusNavigation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">KeyEvent&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">focused&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">findFocus&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">focused&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">focused&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">focusSearch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">direction&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">focused&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Do the math to get the interesting rect&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// of previous focused into the coord system of&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// newly focused view&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">focused&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getFocusedRect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mTempRect&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">instanceof&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">offsetDescendantRectToMyCoords&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">focused&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mTempRect&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">offsetRectIntoDescendantCoords&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mTempRect&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">requestFocus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">direction&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mTempRect&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">isFastScrolling&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getRepeatCount&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">playSoundEffect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SoundEffectConstants&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getConstantForFocusDirection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">direction&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">isFastScrolling&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Give the focused view a last chance to handle the dpad key.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">dispatchUnhandledMove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">focused&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">direction&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">restoreDefaultFocus&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>ViewRootImpl中唯一的一个View是DecorView，开始时找不到焦点，会把焦点给DecorView。DecorView会在requestFocus中分发下去，最终焦点会给第一个满足条件的View。&lt;/p>
&lt;p>在这里ViewGroup需要策略和顺序。
策略分别有：&lt;/p>
&lt;ul>
&lt;li>FOCUS_BLOCK_DESCENDANTS: 不给子View焦点，即使该View没有获取到焦点&lt;/li>
&lt;li>FOCUS_BEFORE_DESCENDANTS: 先于子View获取焦点，失败后才尝试给子View&lt;/li>
&lt;li>FOCUS_AFTER_DESCENDANTS: 所有子View获取焦点失败后，才尝试获取焦点&lt;/li>
&lt;/ul>
&lt;p>分发的方向是根据ViewRootImpl分发的事件的KeyEvent。&lt;/p>
&lt;p>这就是第一次点击时焦点分发的过程。&lt;/p>
&lt;p>第二次点击时，拿到上次得到焦点的View。也会按照这次的分发的方向找到一个应该获取焦点的View。&lt;/p>
&lt;h3 class="heading" id="焦点的条件">
 焦点的条件&lt;span class="heading__anchor"> &lt;a href="#%e7%84%a6%e7%82%b9%e7%9a%84%e6%9d%a1%e4%bb%b6">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>View需要满足以下几个条件时才有可能获取到焦点。&lt;/p>
&lt;ol>
&lt;li>可见&lt;/li>
&lt;li>focusable&lt;/li>
&lt;li>enable&lt;/li>
&lt;li>size &amp;gt; 0&lt;/li>
&lt;/ol>
&lt;h3 class="heading" id="转换">
 转换&lt;span class="heading__anchor"> &lt;a href="#%e8%bd%ac%e6%8d%a2">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>两个View不同时用到了两个方法：&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>方法名&lt;/th>
 &lt;th>转换方向&lt;/th>
 &lt;th>常用场景&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>​&lt;code>​offsetDescendantRectToMyCoords&lt;/code>​&lt;/td>
 &lt;td>从&lt;strong>子视图&lt;/strong>坐标系到&lt;strong>当前视图&lt;/strong>坐标系&lt;/td>
 &lt;td>计算子视图在当前视图中的具体位置&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>​&lt;code>​offsetRectIntoDescendantCoords&lt;/code>​&lt;/td>
 &lt;td>从&lt;strong>当前视图&lt;/strong>坐标系到&lt;strong>子视图&lt;/strong>坐标系&lt;/td>
 &lt;td>计算当前视图的某区域相对于子视图的位置&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>结合起来也就是把两个View的坐标转换成一致的，把这个矩形给下去，以便在View的onFocusChanged可以用到，比如ListView需要显示有焦点的View，需要移动。&lt;/p></description></item><item><title>ADB protocol</title><link>https://opoojkk.com/posts/2025/01/04/adb-protocol/</link><pubDate>Sat, 04 Jan 2025 09:10:44 +0800</pubDate><guid>https://opoojkk.com/posts/2025/01/04/adb-protocol/</guid><description>&lt;h2 class="heading" id="说明">
 说明&lt;span class="heading__anchor"> &lt;a href="#%e8%af%b4%e6%98%8e">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>网上太多文章写ADB，不尽详细，大多一知半解，干脆找来官方文件。&lt;/p>
&lt;p>你最好按照下面的顺序阅读：&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-overview/" title="OVERVIEW.txt">adb overview&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-readme/" title="README.md">adb readme&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-services/" title="SERVICES.txt">adb service&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-protocol/" title="protocol.txt">adb protocol&lt;/a>&lt;/li>
&lt;/ol>
&lt;p>三篇都是复制自AOSP13源码，无修改，请放心阅读。&lt;/p>
&lt;h2 class="heading" id="原文">
 原文&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e6%96%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;span class="lnt">124
&lt;/span>&lt;span class="lnt">125
&lt;/span>&lt;span class="lnt">126
&lt;/span>&lt;span class="lnt">127
&lt;/span>&lt;span class="lnt">128
&lt;/span>&lt;span class="lnt">129
&lt;/span>&lt;span class="lnt">130
&lt;/span>&lt;span class="lnt">131
&lt;/span>&lt;span class="lnt">132
&lt;/span>&lt;span class="lnt">133
&lt;/span>&lt;span class="lnt">134
&lt;/span>&lt;span class="lnt">135
&lt;/span>&lt;span class="lnt">136
&lt;/span>&lt;span class="lnt">137
&lt;/span>&lt;span class="lnt">138
&lt;/span>&lt;span class="lnt">139
&lt;/span>&lt;span class="lnt">140
&lt;/span>&lt;span class="lnt">141
&lt;/span>&lt;span class="lnt">142
&lt;/span>&lt;span class="lnt">143
&lt;/span>&lt;span class="lnt">144
&lt;/span>&lt;span class="lnt">145
&lt;/span>&lt;span class="lnt">146
&lt;/span>&lt;span class="lnt">147
&lt;/span>&lt;span class="lnt">148
&lt;/span>&lt;span class="lnt">149
&lt;/span>&lt;span class="lnt">150
&lt;/span>&lt;span class="lnt">151
&lt;/span>&lt;span class="lnt">152
&lt;/span>&lt;span class="lnt">153
&lt;/span>&lt;span class="lnt">154
&lt;/span>&lt;span class="lnt">155
&lt;/span>&lt;span class="lnt">156
&lt;/span>&lt;span class="lnt">157
&lt;/span>&lt;span class="lnt">158
&lt;/span>&lt;span class="lnt">159
&lt;/span>&lt;span class="lnt">160
&lt;/span>&lt;span class="lnt">161
&lt;/span>&lt;span class="lnt">162
&lt;/span>&lt;span class="lnt">163
&lt;/span>&lt;span class="lnt">164
&lt;/span>&lt;span class="lnt">165
&lt;/span>&lt;span class="lnt">166
&lt;/span>&lt;span class="lnt">167
&lt;/span>&lt;span class="lnt">168
&lt;/span>&lt;span class="lnt">169
&lt;/span>&lt;span class="lnt">170
&lt;/span>&lt;span class="lnt">171
&lt;/span>&lt;span class="lnt">172
&lt;/span>&lt;span class="lnt">173
&lt;/span>&lt;span class="lnt">174
&lt;/span>&lt;span class="lnt">175
&lt;/span>&lt;span class="lnt">176
&lt;/span>&lt;span class="lnt">177
&lt;/span>&lt;span class="lnt">178
&lt;/span>&lt;span class="lnt">179
&lt;/span>&lt;span class="lnt">180
&lt;/span>&lt;span class="lnt">181
&lt;/span>&lt;span class="lnt">182
&lt;/span>&lt;span class="lnt">183
&lt;/span>&lt;span class="lnt">184
&lt;/span>&lt;span class="lnt">185
&lt;/span>&lt;span class="lnt">186
&lt;/span>&lt;span class="lnt">187
&lt;/span>&lt;span class="lnt">188
&lt;/span>&lt;span class="lnt">189
&lt;/span>&lt;span class="lnt">190
&lt;/span>&lt;span class="lnt">191
&lt;/span>&lt;span class="lnt">192
&lt;/span>&lt;span class="lnt">193
&lt;/span>&lt;span class="lnt">194
&lt;/span>&lt;span class="lnt">195
&lt;/span>&lt;span class="lnt">196
&lt;/span>&lt;span class="lnt">197
&lt;/span>&lt;span class="lnt">198
&lt;/span>&lt;span class="lnt">199
&lt;/span>&lt;span class="lnt">200
&lt;/span>&lt;span class="lnt">201
&lt;/span>&lt;span class="lnt">202
&lt;/span>&lt;span class="lnt">203
&lt;/span>&lt;span class="lnt">204
&lt;/span>&lt;span class="lnt">205
&lt;/span>&lt;span class="lnt">206
&lt;/span>&lt;span class="lnt">207
&lt;/span>&lt;span class="lnt">208
&lt;/span>&lt;span class="lnt">209
&lt;/span>&lt;span class="lnt">210
&lt;/span>&lt;span class="lnt">211
&lt;/span>&lt;span class="lnt">212
&lt;/span>&lt;span class="lnt">213
&lt;/span>&lt;span class="lnt">214
&lt;/span>&lt;span class="lnt">215
&lt;/span>&lt;span class="lnt">216
&lt;/span>&lt;span class="lnt">217
&lt;/span>&lt;span class="lnt">218
&lt;/span>&lt;span class="lnt">219
&lt;/span>&lt;span class="lnt">220
&lt;/span>&lt;span class="lnt">221
&lt;/span>&lt;span class="lnt">222
&lt;/span>&lt;span class="lnt">223
&lt;/span>&lt;span class="lnt">224
&lt;/span>&lt;span class="lnt">225
&lt;/span>&lt;span class="lnt">226
&lt;/span>&lt;span class="lnt">227
&lt;/span>&lt;span class="lnt">228
&lt;/span>&lt;span class="lnt">229
&lt;/span>&lt;span class="lnt">230
&lt;/span>&lt;span class="lnt">231
&lt;/span>&lt;span class="lnt">232
&lt;/span>&lt;span class="lnt">233
&lt;/span>&lt;span class="lnt">234
&lt;/span>&lt;span class="lnt">235
&lt;/span>&lt;span class="lnt">236
&lt;/span>&lt;span class="lnt">237
&lt;/span>&lt;span class="lnt">238
&lt;/span>&lt;span class="lnt">239
&lt;/span>&lt;span class="lnt">240
&lt;/span>&lt;span class="lnt">241
&lt;/span>&lt;span class="lnt">242
&lt;/span>&lt;span class="lnt">243
&lt;/span>&lt;span class="lnt">244
&lt;/span>&lt;span class="lnt">245
&lt;/span>&lt;span class="lnt">246
&lt;/span>&lt;span class="lnt">247
&lt;/span>&lt;span class="lnt">248
&lt;/span>&lt;span class="lnt">249
&lt;/span>&lt;span class="lnt">250
&lt;/span>&lt;span class="lnt">251
&lt;/span>&lt;span class="lnt">252
&lt;/span>&lt;span class="lnt">253
&lt;/span>&lt;span class="lnt">254
&lt;/span>&lt;span class="lnt">255
&lt;/span>&lt;span class="lnt">256
&lt;/span>&lt;span class="lnt">257
&lt;/span>&lt;span class="lnt">258
&lt;/span>&lt;span class="lnt">259
&lt;/span>&lt;span class="lnt">260
&lt;/span>&lt;span class="lnt">261
&lt;/span>&lt;span class="lnt">262
&lt;/span>&lt;span class="lnt">263
&lt;/span>&lt;span class="lnt">264
&lt;/span>&lt;span class="lnt">265
&lt;/span>&lt;span class="lnt">266
&lt;/span>&lt;span class="lnt">267
&lt;/span>&lt;span class="lnt">268
&lt;/span>&lt;span class="lnt">269
&lt;/span>&lt;span class="lnt">270
&lt;/span>&lt;span class="lnt">271
&lt;/span>&lt;span class="lnt">272
&lt;/span>&lt;span class="lnt">273
&lt;/span>&lt;span class="lnt">274
&lt;/span>&lt;span class="lnt">275
&lt;/span>&lt;span class="lnt">276
&lt;/span>&lt;span class="lnt">277
&lt;/span>&lt;span class="lnt">278
&lt;/span>&lt;span class="lnt">279
&lt;/span>&lt;span class="lnt">280
&lt;/span>&lt;span class="lnt">281
&lt;/span>&lt;span class="lnt">282
&lt;/span>&lt;span class="lnt">283
&lt;/span>&lt;span class="lnt">284
&lt;/span>&lt;span class="lnt">285
&lt;/span>&lt;span class="lnt">286
&lt;/span>&lt;span class="lnt">287
&lt;/span>&lt;span class="lnt">288
&lt;/span>&lt;span class="lnt">289
&lt;/span>&lt;span class="lnt">290
&lt;/span>&lt;span class="lnt">291
&lt;/span>&lt;span class="lnt">292
&lt;/span>&lt;span class="lnt">293
&lt;/span>&lt;span class="lnt">294
&lt;/span>&lt;span class="lnt">295
&lt;/span>&lt;span class="lnt">296
&lt;/span>&lt;span class="lnt">297
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-plain" data-lang="plain">&lt;span class="line">&lt;span class="cl">--- a replacement for aproto -------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">When it comes down to it, aproto&amp;#39;s primary purpose is to forward
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">various streams between the host computer and client device (in either
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">direction).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">This replacement further simplifies the concept, reducing the protocol
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to an extremely straightforward model optimized to accomplish the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">forwarding of these streams and removing additional state or
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">complexity.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The host side becomes a simple comms bridge with no &amp;#34;UI&amp;#34;, which will 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">be used by either commandline or interactive tools to communicate with 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a device or emulator that is connected to the bridge.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The protocol is designed to be straightforward and well-defined enough 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">that if it needs to be reimplemented in another environment (Java 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">perhaps), there should not problems ensuring perfect interoperability.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The protocol discards the layering aproto has and should allow the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">implementation to be much more robust.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- protocol overview and basics ---------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The transport layer deals in &amp;#34;messages&amp;#34;, which consist of a 24 byte
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">header followed (optionally) by a payload. The header consists of 6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">32 bit words which are sent across the wire in little endian format.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">struct message {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> unsigned command; /* command identifier constant (A_CNXN, ...) */
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> unsigned arg0; /* first argument */
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> unsigned arg1; /* second argument */
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> unsigned data_length; /* length of payload (0 is allowed) */
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> unsigned data_crc32; /* crc32 of data payload */
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> unsigned magic; /* command ^ 0xffffffff */
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">};
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Receipt of an invalid message header, corrupt message payload, or an
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">unrecognized command MUST result in the closing of the remote
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">connection. The protocol depends on shared state and any break in the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">message stream will result in state getting out of sync.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The following sections describe the six defined message types in
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">detail. Their format is COMMAND(arg0, arg1, payload) where the payload
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">is represented by a quoted string or an empty string if none should be
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sent.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The identifiers &amp;#34;local-id&amp;#34; and &amp;#34;remote-id&amp;#34; are always relative to the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*sender* of the message, so for a receiver, the meanings are effectively
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">reversed.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- CONNECT(version, maxdata, &amp;#34;system-identity-string&amp;#34;) ----------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Command constant: A_CNXN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The CONNECT message establishes the presence of a remote system.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The version is used to ensure protocol compatibility and maxdata
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">declares the maximum message body size that the remote system
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">is willing to accept.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Currently, version=0x01000000 and maxdata=256*1024. Older versions of adb
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hard-coded maxdata=4096, so CONNECT and AUTH packets sent to a device must not
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">be larger than that because they&amp;#39;re sent before the CONNECT from the device
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">that tells the adb server what maxdata the device can support.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Both sides send a CONNECT message when the connection between them is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">established. Until a CONNECT message is received no other messages may
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">be sent. Any messages received before a CONNECT message MUST be ignored.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">If a CONNECT message is received with an unknown version or insufficiently
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">large maxdata value, the connection with the other side must be closed.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The system identity string should be &amp;#34;&amp;lt;systemtype&amp;gt;:&amp;lt;serialno&amp;gt;:&amp;lt;banner&amp;gt;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">where systemtype is &amp;#34;bootloader&amp;#34;, &amp;#34;device&amp;#34;, or &amp;#34;host&amp;#34;, serialno is some
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">kind of unique ID (or empty), and banner is a human-readable version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">or identifier string. The banner is used to transmit useful properties.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- STLS(type, version, &amp;#34;&amp;#34;) --------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Command constant: A_STLS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The TLS message informs the recipient that the connection will be encrypted
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">and will need to perform a TLS handshake. version is the current version of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the protocol.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- AUTH(type, 0, &amp;#34;data&amp;#34;) ----------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Command constant: A_AUTH
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The AUTH message informs the recipient that authentication is required to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">connect to the sender. If type is TOKEN(1), data is a random token that
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the recipient can sign with a private key. The recipient replies with an
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">AUTH packet where type is SIGNATURE(2) and data is the signature. If the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">signature verification succeeds, the sender replies with a CONNECT packet.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">If the signature verification fails, the sender replies with a new AUTH
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">packet and a new random token, so that the recipient can retry signing
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">with a different private key.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Once the recipient has tried all its private keys, it can reply with an
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">AUTH packet where type is RSAPUBLICKEY(3) and data is the public key. If
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">possible, an on-screen confirmation may be displayed for the user to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">confirm they want to install the public key on the device.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- OPEN(local-id, 0, &amp;#34;destination&amp;#34;) -----------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Command constant: A_OPEN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The OPEN message informs the recipient that the sender has a stream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">identified by local-id that it wishes to connect to the named
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">destination in the message payload. The local-id may not be zero.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The OPEN message MUST result in either a READY message indicating that
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the connection has been established (and identifying the other end) or
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a CLOSE message, indicating failure. An OPEN message also implies
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a READY message sent at the same time.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Common destination naming conventions include:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* &amp;#34;tcp:&amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;&amp;#34; - host may be omitted to indicate localhost
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* &amp;#34;udp:&amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;&amp;#34; - host may be omitted to indicate localhost
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* &amp;#34;vsock:&amp;lt;CID&amp;gt;:&amp;lt;port&amp;gt;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* &amp;#34;local-dgram:&amp;lt;identifier&amp;gt;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* &amp;#34;local-stream:&amp;lt;identifier&amp;gt;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* &amp;#34;shell&amp;#34; - local shell service
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* &amp;#34;upload&amp;#34; - service for pushing files across (like aproto&amp;#39;s /sync)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* &amp;#34;fs-bridge&amp;#34; - FUSE protocol filesystem bridge
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- READY(local-id, remote-id, &amp;#34;&amp;#34;) -------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Command constant: A_OKAY
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The READY message informs the recipient that the sender&amp;#39;s stream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">identified by local-id is ready for write messages and that it is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">connected to the recipient&amp;#39;s stream identified by remote-id.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Neither the local-id nor the remote-id may be zero.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">A READY message containing a remote-id which does not map to an open
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">stream on the recipient&amp;#39;s side is ignored. The stream may have been
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">closed while this message was in-flight.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The local-id is ignored on all but the first READY message (where it
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">is used to establish the connection). Nonetheless, the local-id MUST
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">not change on later READY messages sent to the same stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- WRITE(local-id, remote-id, &amp;#34;data&amp;#34;) ---------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Command constant: A_WRTE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The WRITE message sends data to the recipient&amp;#39;s stream identified by
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">remote-id. The payload MUST be &amp;lt;= maxdata in length.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">A WRITE message containing a remote-id which does not map to an open
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">stream on the recipient&amp;#39;s side is ignored. The stream may have been
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">closed while this message was in-flight.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">A WRITE message may not be sent until a READY message is received.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Once a WRITE message is sent, an additional WRITE message may not be
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sent until another READY message has been received. Recipients of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a WRITE message that is in violation of this requirement will CLOSE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the connection.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- CLOSE(local-id, remote-id, &amp;#34;&amp;#34;) -------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Command constant: A_CLSE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The CLOSE message informs recipient that the connection between the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sender&amp;#39;s stream (local-id) and the recipient&amp;#39;s stream (remote-id) is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">broken. The remote-id MUST not be zero, but the local-id MAY be zero
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">if this CLOSE indicates a failed OPEN.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">A CLOSE message containing a remote-id which does not map to an open
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">stream on the recipient&amp;#39;s side is ignored. The stream may have
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">already been closed by the recipient while this message was in-flight.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The recipient should not respond to a CLOSE message in any way. The
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">recipient should cancel pending WRITEs or CLOSEs, but this is not a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">requirement, since they will be ignored.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- SYNC(online, sequence, &amp;#34;&amp;#34;) -----------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Command constant: A_SYNC
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*** obsolete, no longer used ***
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The SYNC message was used by the io pump to make sure that stale
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">outbound messages are discarded when the connection to the remote side
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">is broken. It was only used internally to the bridge and never valid
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to send across the wire.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* when the connection to the remote side goes offline, the io pump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sends a SYNC(0, 0) and starts discarding all messages
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* when the connection to the remote side is established, the io pump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sends a SYNC(1, token) and continues to discard messages
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* when the io pump receives a matching SYNC(1, token), it once again
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> starts accepting messages to forward to the remote side
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- message command constants ------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">#define A_SYNC 0x434e5953
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">#define A_CNXN 0x4e584e43
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">#define A_AUTH 0x48545541
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">#define A_OPEN 0x4e45504f
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">#define A_OKAY 0x59414b4f
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">#define A_CLSE 0x45534c43
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">#define A_WRTE 0x45545257
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">#define A_STLS 0x534C5453
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- implementation details ---------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The core of the bridge program will use three threads. One thread
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">will be a select/epoll loop to handle io between various inbound and
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">outbound connections and the connection to the remote side.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The remote side connection will be implemented as two threads (one for
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">reading, one for writing) and a datagram socketpair to provide the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">channel between the main select/epoll thread and the remote connection
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">threadpair. The reason for this is that for usb connections, the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">kernel interface on linux and osx does not allow you to do meaningful
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">nonblocking IO.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The endian swapping for the message headers will happen (as needed) in
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the remote connection threadpair and that the rest of the program will
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">always treat message header values as native-endian.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The bridge program will be able to have a number of mini-servers
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">compiled in. They will be published under known names (examples
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;shell&amp;#34;, &amp;#34;fs-bridge&amp;#34;, etc) and upon receiving an OPEN() to such a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service, the bridge program will create a stream socketpair and spawn
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a thread or subprocess to handle the io.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- simplified / embedded implementation -------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">For limited environments, like the bootloader, it is allowable to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">support a smaller, fixed number of channels using pre-assigned channel
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ID numbers such that only one stream may be connected to a bootloader
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">endpoint at any given time. The protocol remains unchanged, but the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;embedded&amp;#34; version of it is less dynamic.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The bootloader will support two streams. A &amp;#34;bootloader:debug&amp;#34; stream,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">which may be opened to get debug messages from the bootloader and a 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;bootloader:control&amp;#34;, stream which will support the set of basic 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bootloader commands.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Example command stream dialogues: 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;flash_kernel,2515049,........\n&amp;#34; &amp;#34;okay\n&amp;#34; 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;flash_ramdisk,5038,........\n&amp;#34; &amp;#34;fail,flash write error\n&amp;#34; 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;bogus_command......&amp;#34; &amp;lt;CLOSE&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- future expansion ---------------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">I plan on providing either a message or a special control stream so that
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the client device could ask the host computer to setup inbound socket
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">translations on the fly on behalf of the client device.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The initial design does handshaking to provide flow control, with a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">message flow that looks like:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;gt;OPEN &amp;lt;READY &amp;gt;WRITE &amp;lt;READY &amp;gt;WRITE &amp;lt;READY &amp;gt;WRITE &amp;lt;CLOSE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The far side may choose to issue the READY message as soon as it receives
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a WRITE or it may defer the READY until the write to the local stream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">succeeds. A future version may want to do some level of windowing where
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">multiple WRITEs may be sent without requiring individual READY acks.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- smartsockets -------------------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Port 5037 is used for smart sockets which allow a client on the host
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">side to request access to a service in the host adb daemon or in the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">remote (device) daemon. The service is requested by ascii name,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">preceeded by a 4 digit hex length. Upon successful connection an
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;OKAY&amp;#34; response is sent, otherwise a &amp;#34;FAIL&amp;#34; message is returned. Once
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">connected the client is talking to that (remote or local) service.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">client: &amp;lt;hex4&amp;gt; &amp;lt;service-name&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">server: &amp;#34;OKAY&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">client: &amp;lt;hex4&amp;gt; &amp;lt;service-name&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">server: &amp;#34;FAIL&amp;#34; &amp;lt;hex4&amp;gt; &amp;lt;reason&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>ADB SERVICES</title><link>https://opoojkk.com/posts/2025/01/04/adb-services/</link><pubDate>Sat, 04 Jan 2025 09:06:42 +0800</pubDate><guid>https://opoojkk.com/posts/2025/01/04/adb-services/</guid><description>&lt;h2 class="heading" id="说明">
 说明&lt;span class="heading__anchor"> &lt;a href="#%e8%af%b4%e6%98%8e">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>网上太多文章写ADB，不尽详细，大多一知半解，干脆找来官方文件。&lt;/p>
&lt;p>你最好按照下面的顺序阅读：&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-overview/" title="OVERVIEW.txt">adb overview&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-readme/" title="README.md">adb readme&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-services/" title="SERVICES.txt">adb service&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-protocol/" title="protocol.txt">adb protocol&lt;/a>&lt;/li>
&lt;/ol>
&lt;p>三篇都是复制自AOSP13源码，无修改，请放心阅读。&lt;/p>
&lt;h2 class="heading" id="原文">
 原文&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e6%96%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;span class="lnt">124
&lt;/span>&lt;span class="lnt">125
&lt;/span>&lt;span class="lnt">126
&lt;/span>&lt;span class="lnt">127
&lt;/span>&lt;span class="lnt">128
&lt;/span>&lt;span class="lnt">129
&lt;/span>&lt;span class="lnt">130
&lt;/span>&lt;span class="lnt">131
&lt;/span>&lt;span class="lnt">132
&lt;/span>&lt;span class="lnt">133
&lt;/span>&lt;span class="lnt">134
&lt;/span>&lt;span class="lnt">135
&lt;/span>&lt;span class="lnt">136
&lt;/span>&lt;span class="lnt">137
&lt;/span>&lt;span class="lnt">138
&lt;/span>&lt;span class="lnt">139
&lt;/span>&lt;span class="lnt">140
&lt;/span>&lt;span class="lnt">141
&lt;/span>&lt;span class="lnt">142
&lt;/span>&lt;span class="lnt">143
&lt;/span>&lt;span class="lnt">144
&lt;/span>&lt;span class="lnt">145
&lt;/span>&lt;span class="lnt">146
&lt;/span>&lt;span class="lnt">147
&lt;/span>&lt;span class="lnt">148
&lt;/span>&lt;span class="lnt">149
&lt;/span>&lt;span class="lnt">150
&lt;/span>&lt;span class="lnt">151
&lt;/span>&lt;span class="lnt">152
&lt;/span>&lt;span class="lnt">153
&lt;/span>&lt;span class="lnt">154
&lt;/span>&lt;span class="lnt">155
&lt;/span>&lt;span class="lnt">156
&lt;/span>&lt;span class="lnt">157
&lt;/span>&lt;span class="lnt">158
&lt;/span>&lt;span class="lnt">159
&lt;/span>&lt;span class="lnt">160
&lt;/span>&lt;span class="lnt">161
&lt;/span>&lt;span class="lnt">162
&lt;/span>&lt;span class="lnt">163
&lt;/span>&lt;span class="lnt">164
&lt;/span>&lt;span class="lnt">165
&lt;/span>&lt;span class="lnt">166
&lt;/span>&lt;span class="lnt">167
&lt;/span>&lt;span class="lnt">168
&lt;/span>&lt;span class="lnt">169
&lt;/span>&lt;span class="lnt">170
&lt;/span>&lt;span class="lnt">171
&lt;/span>&lt;span class="lnt">172
&lt;/span>&lt;span class="lnt">173
&lt;/span>&lt;span class="lnt">174
&lt;/span>&lt;span class="lnt">175
&lt;/span>&lt;span class="lnt">176
&lt;/span>&lt;span class="lnt">177
&lt;/span>&lt;span class="lnt">178
&lt;/span>&lt;span class="lnt">179
&lt;/span>&lt;span class="lnt">180
&lt;/span>&lt;span class="lnt">181
&lt;/span>&lt;span class="lnt">182
&lt;/span>&lt;span class="lnt">183
&lt;/span>&lt;span class="lnt">184
&lt;/span>&lt;span class="lnt">185
&lt;/span>&lt;span class="lnt">186
&lt;/span>&lt;span class="lnt">187
&lt;/span>&lt;span class="lnt">188
&lt;/span>&lt;span class="lnt">189
&lt;/span>&lt;span class="lnt">190
&lt;/span>&lt;span class="lnt">191
&lt;/span>&lt;span class="lnt">192
&lt;/span>&lt;span class="lnt">193
&lt;/span>&lt;span class="lnt">194
&lt;/span>&lt;span class="lnt">195
&lt;/span>&lt;span class="lnt">196
&lt;/span>&lt;span class="lnt">197
&lt;/span>&lt;span class="lnt">198
&lt;/span>&lt;span class="lnt">199
&lt;/span>&lt;span class="lnt">200
&lt;/span>&lt;span class="lnt">201
&lt;/span>&lt;span class="lnt">202
&lt;/span>&lt;span class="lnt">203
&lt;/span>&lt;span class="lnt">204
&lt;/span>&lt;span class="lnt">205
&lt;/span>&lt;span class="lnt">206
&lt;/span>&lt;span class="lnt">207
&lt;/span>&lt;span class="lnt">208
&lt;/span>&lt;span class="lnt">209
&lt;/span>&lt;span class="lnt">210
&lt;/span>&lt;span class="lnt">211
&lt;/span>&lt;span class="lnt">212
&lt;/span>&lt;span class="lnt">213
&lt;/span>&lt;span class="lnt">214
&lt;/span>&lt;span class="lnt">215
&lt;/span>&lt;span class="lnt">216
&lt;/span>&lt;span class="lnt">217
&lt;/span>&lt;span class="lnt">218
&lt;/span>&lt;span class="lnt">219
&lt;/span>&lt;span class="lnt">220
&lt;/span>&lt;span class="lnt">221
&lt;/span>&lt;span class="lnt">222
&lt;/span>&lt;span class="lnt">223
&lt;/span>&lt;span class="lnt">224
&lt;/span>&lt;span class="lnt">225
&lt;/span>&lt;span class="lnt">226
&lt;/span>&lt;span class="lnt">227
&lt;/span>&lt;span class="lnt">228
&lt;/span>&lt;span class="lnt">229
&lt;/span>&lt;span class="lnt">230
&lt;/span>&lt;span class="lnt">231
&lt;/span>&lt;span class="lnt">232
&lt;/span>&lt;span class="lnt">233
&lt;/span>&lt;span class="lnt">234
&lt;/span>&lt;span class="lnt">235
&lt;/span>&lt;span class="lnt">236
&lt;/span>&lt;span class="lnt">237
&lt;/span>&lt;span class="lnt">238
&lt;/span>&lt;span class="lnt">239
&lt;/span>&lt;span class="lnt">240
&lt;/span>&lt;span class="lnt">241
&lt;/span>&lt;span class="lnt">242
&lt;/span>&lt;span class="lnt">243
&lt;/span>&lt;span class="lnt">244
&lt;/span>&lt;span class="lnt">245
&lt;/span>&lt;span class="lnt">246
&lt;/span>&lt;span class="lnt">247
&lt;/span>&lt;span class="lnt">248
&lt;/span>&lt;span class="lnt">249
&lt;/span>&lt;span class="lnt">250
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-plain" data-lang="plain">&lt;span class="line">&lt;span class="cl">This file tries to document all requests a client can make
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to the ADB server of an adbd daemon. See the OVERVIEW.TXT document
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to understand what&amp;#39;s going on here.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">HOST SERVICES:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host:version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Ask the ADB server for its internal version number.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host:kill
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Ask the ADB server to quit immediately. This is used when the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ADB client detects that an obsolete server is running after an
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> upgrade.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host:devices
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host:devices-l
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Ask to return the list of available Android devices and their
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> state. devices-l includes the device paths in the state.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> After the OKAY, this is followed by a 4-byte hex len,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and a string that will be dumped as-is by the client, then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the connection is closed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host:track-devices
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This is a variant of host:devices which doesn&amp;#39;t close the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> connection. Instead, a new device list description is sent
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> each time a device is added/removed or the state of a given
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> device changes (hex4 + content). This allows tools like DDMS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to track the state of connected devices in real-time without
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> polling the server repeatedly.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host:emulator:&amp;lt;port&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This is a special query that is sent to the ADB server when a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> new emulator starts up. &amp;lt;port&amp;gt; is a decimal number corresponding
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to the emulator&amp;#39;s ADB control port, i.e. the TCP port that the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> emulator will forward automatically to the adbd daemon running
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in the emulator system.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This mechanism allows the ADB server to know when new emulator
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> instances start.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host:transport:&amp;lt;serial-number&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Ask to switch the connection to the device/emulator identified by
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt;serial-number&amp;gt;. After the OKAY response, every client request will
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> be sent directly to the adbd daemon running on the device.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (Used to implement the -s option)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host:transport-usb
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Ask to switch the connection to one device connected through USB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to the host machine. This will fail if there are more than one such
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> devices. (Used to implement the -d convenience option)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host:transport-local
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Ask to switch the connection to one emulator connected through TCP.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This will fail if there is more than one such emulator instance
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> running. (Used to implement the -e convenience option)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host:transport-any
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Another host:transport variant. Ask to switch the connection to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> either the device or emulator connect to/running on the host.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Will fail if there is more than one such device/emulator available.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (Used when neither -s, -d or -e are provided)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host-serial:&amp;lt;serial-number&amp;gt;:&amp;lt;request&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This is a special form of query, where the &amp;#39;host-serial:&amp;lt;serial-number&amp;gt;:&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> prefix can be used to indicate that the client is asking the ADB server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for information related to a specific device. &amp;lt;request&amp;gt; can be in one
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of the format described below.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host-usb:&amp;lt;request&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> A variant of host-serial used to target the single USB device connected
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to the host. This will fail if there is none or more than one.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host-local:&amp;lt;request&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> A variant of host-serial used to target the single emulator instance
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> running on the host. This will fail if there is none or more than one.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">host:&amp;lt;request&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> When asking for information related to a device, &amp;#39;host:&amp;#39; can also be
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> interpreted as &amp;#39;any single device or emulator connected to/running on
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the host&amp;#39;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;host-prefix&amp;gt;:get-product
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> XXX
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;host-prefix&amp;gt;:get-serialno
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Returns the serial number of the corresponding device/emulator.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note that emulator serial numbers are of the form &amp;#34;emulator-5554&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;host-prefix&amp;gt;:get-devpath
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Returns the device path of the corresponding device/emulator.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;host-prefix&amp;gt;:get-state
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Returns the state of a given device as a string.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;host-prefix&amp;gt;:forward:&amp;lt;local&amp;gt;;&amp;lt;remote&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Asks the ADB server to forward local connections from &amp;lt;local&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to the &amp;lt;remote&amp;gt; address on a given device.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> There, &amp;lt;host-prefix&amp;gt; can be one of the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> host-serial/host-usb/host-local/host prefixes as described previously
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and indicates which device/emulator to target.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the format of &amp;lt;local&amp;gt; is one of:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> tcp:&amp;lt;port&amp;gt; -&amp;gt; TCP connection on localhost:&amp;lt;port&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> local:&amp;lt;path&amp;gt; -&amp;gt; Unix local domain socket on &amp;lt;path&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the format of &amp;lt;remote&amp;gt; is one of:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> tcp:&amp;lt;port&amp;gt; -&amp;gt; TCP localhost:&amp;lt;port&amp;gt; on device
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> local:&amp;lt;path&amp;gt; -&amp;gt; Unix local domain socket on device
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> jdwp:&amp;lt;pid&amp;gt; -&amp;gt; JDWP thread on VM process &amp;lt;pid&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> vsock:&amp;lt;CID&amp;gt;:&amp;lt;port&amp;gt; -&amp;gt; vsock on the given CID and port
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> or even any one of the local services described below.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;host-prefix&amp;gt;:forward:norebind:&amp;lt;local&amp;gt;;&amp;lt;remote&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Same as &amp;lt;host-prefix&amp;gt;:forward:&amp;lt;local&amp;gt;;&amp;lt;remote&amp;gt; except that it will
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> fail it there is already a forward connection from &amp;lt;local&amp;gt;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Used to implement &amp;#39;adb forward --no-rebind &amp;lt;local&amp;gt; &amp;lt;remote&amp;gt;&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;host-prefix&amp;gt;:killforward:&amp;lt;local&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Remove any existing forward local connection from &amp;lt;local&amp;gt;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This is used to implement &amp;#39;adb forward --remove &amp;lt;local&amp;gt;&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;host-prefix&amp;gt;:killforward-all
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Remove all forward network connections.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This is used to implement &amp;#39;adb forward --remove-all&amp;#39;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;host-prefix&amp;gt;:list-forward
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> List all existing forward connections from this server.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This returns something that looks like the following:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt;hex4&amp;gt;: The length of the payload, as 4 hexadecimal chars.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt;payload&amp;gt;: A series of lines of the following format:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt;serial&amp;gt; &amp;#34; &amp;#34; &amp;lt;local&amp;gt; &amp;#34; &amp;#34; &amp;lt;remote&amp;gt; &amp;#34;\n&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Where &amp;lt;serial&amp;gt; is a device serial number.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt;local&amp;gt; is the host-specific endpoint (e.g. tcp:9000).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt;remote&amp;gt; is the device-specific endpoint.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Used to implement &amp;#39;adb forward --list&amp;#39;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">LOCAL SERVICES:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">All the queries below assumed that you already switched the transport
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to a real device, or that you have used a query prefix as described
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">above.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">shell:command arg1 arg2 ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Run &amp;#39;command arg1 arg2 ...&amp;#39; in a shell on the device, and return
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> its output and error streams. Note that arguments must be separated
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> by spaces. If an argument contains a space, it must be quoted with
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> double-quotes. Arguments cannot contain double quotes or things
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> will go very wrong.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note that this is the non-interactive version of &amp;#34;adb shell&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">shell:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Start an interactive shell session on the device. Redirect
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> stdin/stdout/stderr as appropriate. Note that the ADB server uses
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this to implement &amp;#34;adb shell&amp;#34;, but will also cook the input before
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sending it to the device (see interactive_shell() in commandline.c)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">remount:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Ask adbd to remount the device&amp;#39;s filesystem in read-write mode,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> instead of read-only. This is usually necessary before performing
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> an &amp;#34;adb sync&amp;#34; or &amp;#34;adb push&amp;#34; request.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This request may not succeed on certain builds which do not allow
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> that.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tcp:&amp;lt;port&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Tries to connect to tcp port &amp;lt;port&amp;gt; on localhost.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tcp:&amp;lt;port&amp;gt;:&amp;lt;server-name&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Tries to connect to tcp port &amp;lt;port&amp;gt; on machine &amp;lt;server-name&amp;gt; from
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the device. This can be useful to debug some networking/proxy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> issues that can only be revealed on the device itself.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">local:&amp;lt;path&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Tries to connect to a Unix domain socket &amp;lt;path&amp;gt; on the device
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">localreserved:&amp;lt;path&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">localabstract:&amp;lt;path&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">localfilesystem:&amp;lt;path&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Variants of local:&amp;lt;path&amp;gt; that are used to access other Android
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> socket namespaces.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">framebuffer:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This service is used to send snapshots of the framebuffer to a client.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> It requires sufficient privileges but works as follow:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> After the OKAY, the service sends 16-byte binary structure
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> containing the following fields (little-endian format):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> depth: uint32_t: framebuffer depth
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> size: uint32_t: framebuffer size in bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> width: uint32_t: framebuffer width in pixels
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> height: uint32_t: framebuffer height in pixels
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> With the current implementation, depth is always 16, and
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> size is always width*height*2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Then, each time the client wants a snapshot, it should send
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> one byte through the channel, which will trigger the service
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to send it &amp;#39;size&amp;#39; bytes of framebuffer data.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> If the adbd daemon doesn&amp;#39;t have sufficient privileges to open
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the framebuffer device, the connection is simply closed immediately.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">jdwp:&amp;lt;pid&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Connects to the JDWP thread running in the VM of process &amp;lt;pid&amp;gt;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">track-jdwp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This is used to send the list of JDWP pids periodically to the client.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The format of the returned data is the following:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt;hex4&amp;gt;: the length of all content as a 4-char hexadecimal string
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt;content&amp;gt;: a series of ASCII lines of the following format:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt;pid&amp;gt; &amp;#34;\n&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This service is used by DDMS to know which debuggable processes are running
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> on the device/emulator.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note that there is no single-shot service to retrieve the list only once.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sync:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This starts the file synchronization service, used to implement &amp;#34;adb push&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and &amp;#34;adb pull&amp;#34;. Since this service is pretty complex, it will be detailed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in a companion document named SYNC.TXT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">reverse:&amp;lt;forward-command&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This implements the &amp;#39;adb reverse&amp;#39; feature, i.e. the ability to reverse
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> socket connections from a device to the host. &amp;lt;forward-command&amp;gt; is one
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of the forwarding commands that are described above, as in:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> list-forward
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> forward:&amp;lt;local&amp;gt;;&amp;lt;remote&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> forward:norebind:&amp;lt;local&amp;gt;;&amp;lt;remote&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> killforward-all
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> killforward:&amp;lt;local&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note that in this case, &amp;lt;local&amp;gt; corresponds to the socket on the device
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and &amp;lt;remote&amp;gt; corresponds to the socket on the host.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The output of reverse:list-forward is the same as host:list-forward
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> except that &amp;lt;serial&amp;gt; will be just &amp;#39;host&amp;#39;.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>ADB README</title><link>https://opoojkk.com/posts/2025/01/04/adb-readme/</link><pubDate>Sat, 04 Jan 2025 08:56:06 +0800</pubDate><guid>https://opoojkk.com/posts/2025/01/04/adb-readme/</guid><description>&lt;h2 class="heading" id="说明">
 说明&lt;span class="heading__anchor"> &lt;a href="#%e8%af%b4%e6%98%8e">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>网上太多文章写ADB，不尽详细，大多一知半解，干脆找来官方文件。&lt;/p>
&lt;p>你最好按照下面的顺序阅读：&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-overview/" title="OVERVIEW.txt">adb overview&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-readme/" title="README.md">adb readme&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-services/" title="SERVICES.txt">adb service&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-protocol/" title="protocol.txt">adb protocol&lt;/a>&lt;/li>
&lt;/ol>
&lt;p>三篇都是复制自AOSP13源码，无修改，请放心阅读。&lt;/p>
&lt;h2 class="heading" id="原文">
 原文&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e6%96%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h1 class="heading" id="adb-internals">
 ADB Internals&lt;span class="heading__anchor"> &lt;a href="#adb-internals">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>If you are new to adb source code, you should start by reading &lt;a href="OVERVIEW.TXT">OVERVIEW.TXT&lt;/a> which describes the three components of adb pipeline.&lt;/p>
&lt;p>This document is here to boost what can be achieved within a &amp;ldquo;window of naive interest&amp;rdquo;. You will not find function or class documentation here but rather the &amp;ldquo;big picture&amp;rdquo; which should allow you to build a mental map to help navigate the code.&lt;/p>
&lt;h2 class="heading" id="three-components-of-adb-pipeline">
 Three components of adb pipeline&lt;span class="heading__anchor"> &lt;a href="#three-components-of-adb-pipeline">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>As outlined in the overview, this codebase generates three components (Client, Server (a.k.a Host), and Daemon (a.k.a adbd)). The central part is the Server which runs on the Host computer. On one side the Server exposes a &amp;ldquo;Smart Socket&amp;rdquo; to Clients such as adb or DDMLIB. On the other side, the Server continuously monitors for connecting Daemons (as USB devices or TCP emulator). Communication with a device is done with a Transport.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">+----------+ +------------------------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| ADB +----------+ | ADB SERVER | +----------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| CLIENT | | | | (USB)| ADBD |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+----------+ | | Transport+-------------+ (DEVICE) |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | | +----------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+----------- | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| ADB | v + | +----------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| CLIENT +---------&amp;gt;SmartSocket | (USB)| ADBD |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+----------+ ^ | (TCP/IP) Transport+-------------+ (DEVICE) |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | | +----------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+----------+ | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| DDMLIB | | | Transport+--+ +----------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| CLIENT +----------+ | | | (TCP/IP)| ADBD |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+----------+ +------------------------+ +----------|(EMULATOR)|
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> +----------+
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The Client and the Server are contained in the same executable and both run on the Host machine. Code sections specific to the Host is enclosed within &lt;code>ADB_HOST&lt;/code> guard. adbd runs on the Android Device. Daemon specific code is enclosed in &lt;code>!ADB_HOST&lt;/code> but also sometimes with-in &lt;code>__ANDROID__&lt;/code> guard.&lt;/p>
&lt;h2 class="heading" id="smart-socket-and-transport">
 &amp;ldquo;SMART SOCKET&amp;rdquo; and TRANSPORT&lt;span class="heading__anchor"> &lt;a href="#smart-socket-and-transport">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>A smart socket is a simple TCP socket with a smart protocol built on top of it. This is what Clients connect onto from the Host side. The Client must always initiate communication via a human readable request but the response format varies. The smart protocol is documented in &lt;a href="SERVICES.TXT">SERVICES.TXT&lt;/a>.&lt;/p>
&lt;p>On the other side, the Server communicates with a device via a Transport. adb initially targeted devices connecting over USB, which is restricted to a fixed number of data streams. Therefore, adb multiplexes multiple byte streams over a single pipe via Transport. When devices connecting over other mechanisms (e.g. emulators over TCP) were introduced, the existing transport protocol was maintained.&lt;/p>
&lt;h2 class="heading" id="threading-model-and-fdevent-system">
 THREADING MODEL and FDEVENT system&lt;span class="heading__anchor"> &lt;a href="#threading-model-and-fdevent-system">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>At the heart of both the Server and Daemon is a main thread running an fdevent loop, which is a platform-independent abstraction over poll/epoll/WSAPoll monitoring file descriptors events. Requests and services are usually served from the main thread but some service requests result in new threads being spawned.&lt;/p>
&lt;p>To allow for operations to run on the Main thread, fdevent features a RunQueue combined with an interrupt fd to force polling to return.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">+------------+ +-------------------------^
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| RUNQUEUE | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+------------+ | POLLING (Main thread) |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| Function&amp;lt;&amp;gt; | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+------------+ | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| Function&amp;lt;&amp;gt; | ^-^-------^-------^------^^
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+------------+ | | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| ... | | | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+------------+ | | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| | | | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">|============| | | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">|Interrupt fd+------+ +----+ +----+ +----+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+------------+ fd Socket Pipe
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="asocket-apacket-and-amessage">
 ASOCKET, APACKET, and AMESSAGE&lt;span class="heading__anchor"> &lt;a href="#asocket-apacket-and-amessage">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>The asocket, apacket, and amessage constructs exist only to wrap data while it transits on a Transport. An asocket handles a stream of apackets. An apacket consists in a amessage header featuring a command (&lt;code>A_SYNC&lt;/code>, &lt;code>A_OPEN&lt;/code>, &lt;code>A_CLSE&lt;/code>, &lt;code>A_WRTE&lt;/code>, &lt;code>A_OKAY&lt;/code>, &amp;hellip;) followed by a payload (find more documentation in &lt;a href="protocol.txt">protocol.txt&lt;/a>. There is no &lt;code>A_READ&lt;/code> command because an asocket is unidirectional. To model a bi-directional stream, asocket have a peer which go in the opposite direction.&lt;/p>
&lt;p>An asocket features a buffer where the elemental unit is an apacket. If traffic is inbound, the buffer stores the apacket until it is consumed. If the traffic is oubound, the buffer stores apackets until they are sent down the wire (with &lt;code>A_WRTE&lt;/code> commands).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">+---------------------ASocket------------------------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | +----------------APacket Queue------------------+ |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | APacket APacket APacket | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | +--------+ +--------+ +--------+ | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | |AMessage| |AMessage| |AMessage| | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | +--------+ +--------+ +--------+ | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | | | | | | | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | ..... | | | | | | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | | Data | | Data | | Data | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | | | | | | | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | | | | | | | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | +--------+ +--------+ +--------+ | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | | | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> | +-----------------------------------------------+ |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> +---------------------------------------------------+
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This system allows to multiplex data streams on an unique byte stream. Without entering too much into details, the amessage fields arg1 and arg2 are used alike in the TCP protocol where local and remote ports identify an unique stream. Note that unlike TCP which feature an &amp;ldquo;unacknowledged-send window&amp;rdquo;, an apacket is sent only after the previous one has been confirmed to be received.&lt;/p>
&lt;p>The two types of asocket (Remote and Local) differentiate between outbound and inbound traffic.&lt;/p>
&lt;h2 class="heading" id="adbd---appplication-communication">
 adbd &amp;lt;-&amp;gt; APPPLICATION communication&lt;span class="heading__anchor"> &lt;a href="#adbd---appplication-communication">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>This pipeline is detailed in &lt;a href="daemon/jdwp_service.cpp">daemon/jdwp_service.cpp&lt;/a> with ASCII drawings! The JDWP extension implemented by Dalvik/ART are documented in:&lt;/p>
&lt;ul>
&lt;li>platform/dalvik/+/master/docs/debugmon.html&lt;/li>
&lt;li>platform/dalvik/+/master/docs/debugger.html&lt;/li>
&lt;/ul></description></item><item><title>ADB OVERVIEW</title><link>https://opoojkk.com/posts/2025/01/04/adb-overview/</link><pubDate>Sat, 04 Jan 2025 08:55:46 +0800</pubDate><guid>https://opoojkk.com/posts/2025/01/04/adb-overview/</guid><description>&lt;h2 class="heading" id="说明">
 说明&lt;span class="heading__anchor"> &lt;a href="#%e8%af%b4%e6%98%8e">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>网上太多文章写ADB，不尽详细，大多一知半解，干脆找来官方文件。&lt;/p>
&lt;p>你最好按照下面的顺序阅读：&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-overview/" title="OVERVIEW.txt">adb overview&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-readme/" title="README.md">adb readme&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-services/" title="SERVICES.txt">adb service&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://opoojkk.com/posts/2025/01/04/adb-protocol/" title="protocol.txt">adb protocol&lt;/a>&lt;/li>
&lt;/ol>
&lt;p>三篇都是复制自AOSP13源码，无修改，请放心阅读。&lt;/p>
&lt;h2 class="heading" id="原文">
 原文&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e6%96%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;span class="lnt">124
&lt;/span>&lt;span class="lnt">125
&lt;/span>&lt;span class="lnt">126
&lt;/span>&lt;span class="lnt">127
&lt;/span>&lt;span class="lnt">128
&lt;/span>&lt;span class="lnt">129
&lt;/span>&lt;span class="lnt">130
&lt;/span>&lt;span class="lnt">131
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-plain" data-lang="plain">&lt;span class="line">&lt;span class="cl">Implementation notes regarding ADB.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">I. General Overview:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Android Debug Bridge (ADB) is used to:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* keep track of all Android devices and emulators instances
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> connected to or running on a given host developer machine
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* implement various control commands (e.g. &amp;#34;adb shell&amp;#34;, &amp;#34;adb pull&amp;#34;, etc.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for the benefit of clients (command-line users, or helper programs like
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> DDMS). These commands are called &amp;#39;services&amp;#39; in ADB.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">As a whole, everything works through the following components:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. The ADB server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This is a background process that runs on the host machine. Its purpose
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is to sense the USB ports to know when devices are attached/removed,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> as well as when emulator instances start/stop.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> It thus maintains a list of &amp;#34;connected devices&amp;#34; and assigns a &amp;#39;state&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to each one of them: OFFLINE, BOOTLOADER, RECOVERY or ONLINE (more on
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this below).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The ADB server is really one giant multiplexing loop whose purpose is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to orchestrate the exchange of data (packets, really) between clients,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> services and devices.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. The ADB daemon (adbd)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The &amp;#39;adbd&amp;#39; program runs as a background process within an Android device
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> or emulated system. Its purpose is to connect to the ADB server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (through USB for devices, through TCP for emulators) and provide a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> few services for clients that run on the host.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The ADB server considers that a device is ONLINE when it has successfully
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> connected to the adbd program within it. Otherwise, the device is OFFLINE,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> meaning that the ADB server detected a new device/emulator, but could not
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> connect to the adbd daemon.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The BOOTLOADER and RECOVERY states correspond to alternate states of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> devices when they are in the bootloader or recovery mode.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. The ADB command-line client
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The &amp;#39;adb&amp;#39; command-line program is used to run adb commands from a shell
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> or a script. It first tries to locate the ADB server on the host machine,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and will start one automatically if none is found.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Then, the client sends its service requests to the ADB server.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Currently, a single &amp;#39;adb&amp;#39; binary is used for both the server and client.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this makes distribution and starting the server easier.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. Services
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> There are essentially two kinds of services that a client can talk to.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Host Services:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> These services run within the ADB Server and thus do not need to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> communicate with a device at all. A typical example is &amp;#34;adb devices&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> that is used to return the list of currently known devices and their
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> states. There are a few other services, though.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Local Services:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> These services either run within the adbd daemon, or are started by
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> it on the device. The ADB server is used to multiplex streams
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> between the client and the service running in adbd. In this case
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> its role is to initiate the connection, then of being a pass-through
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for the data.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">II. Protocol details:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. Client &amp;lt;-&amp;gt; Server protocol:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This section details the protocol used between ADB clients and the ADB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> server itself. The ADB server listens on TCP:localhost:5037.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> A client sends a request using the following format:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1. A 4-byte hexadecimal string giving the length of the payload
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2. Followed by the payload itself.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> For example, to query the ADB server for its internal version number,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the client will do the following:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1. Connect to tcp:localhost:5037
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2. Send the string &amp;#34;000Chost:version&amp;#34; to the corresponding socket
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The &amp;#39;host:&amp;#39; prefix is used to indicate that the request is addressed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to the server itself (we will talk about other kinds of requests later).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The content length is encoded in ASCII for easier debugging.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The server should answer a request with one of the following:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1. For success, the 4-byte &amp;#34;OKAY&amp;#34; string
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2. For failure, the 4-byte &amp;#34;FAIL&amp;#34; string, followed by a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4-byte hex length, followed by a string giving the reason
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for failure.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note that the connection is still alive after an OKAY, which allows the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> client to make other requests. But in certain cases, an OKAY will even
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> change the state of the connection.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> For example, the case of the &amp;#39;host:transport:&amp;lt;serialnumber&amp;gt;&amp;#39; request,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> where &amp;#39;&amp;lt;serialnumber&amp;gt;&amp;#39; is used to identify a given device/emulator; after
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the &amp;#34;OKAY&amp;#34; answer, all further requests made by the client will go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directly to the corresponding adbd daemon.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The file SERVICES.TXT lists all services currently implemented by ADB.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. Transports:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> An ADB transport models a connection between the ADB server and one device
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> or emulator. There are currently two kinds of transports:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - USB transports, for physical devices through USB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - Local transports, for emulators running on the host, connected to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the server through TCP
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> In theory, it should be possible to write a local transport that proxies
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> a connection between an ADB server and a device/emulator connected to/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> running on another machine. This hasn&amp;#39;t been done yet though.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Each transport can carry one or more multiplexed streams between clients
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and the device/emulator they point to. The ADB server must handle
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> unexpected transport disconnections (e.g. when a device is physically
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> unplugged) properly.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>解决系统界面高度变化——WindowInsets</title><link>https://opoojkk.com/posts/2025/01/01/android-window-insets./</link><pubDate>Wed, 01 Jan 2025 09:03:25 +0800</pubDate><guid>https://opoojkk.com/posts/2025/01/01/android-window-insets./</guid><description>&lt;p>Android开发中，输入框作为最最基础的View之一，使用的频率是非常非常高的。&lt;/p>
&lt;p>有输入框的地方就有软键盘，我知道有softinputmode可以设置软键盘弹起的行为，这只能响应软键盘弹起后整个Activity的应对行为。如果我想要拿到软键盘高度呢，这是softinputmode做不到的。你可能已经想到了监听View的变化，得到一个高度值，超过了某个阈值就认为软键盘已经弹起了。没错，这是之前常用的方式，要是我想软键盘高度会改变，怎么办？&lt;/p>
&lt;h2 class="heading" id="windowinsets">
 WindowInsets&lt;span class="heading__anchor"> &lt;a href="#windowinsets">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>WindowInsets是什么？window是窗口，对Android开发来说很熟悉，inset在Cambridge dictionary上解释为内衬，两个放在一起就是窗口的内衬。窗口的内衬也就是有东西占用了一部分窗口的内容，放上图片更容易理解。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/nvVL93h.png" alt="示意图">&lt;/figure>&lt;p>WindowInsets的Window其实和Window不太一样，Android的内容显示是依赖Window的，应用中状态栏、导航栏虽然视觉效果是在应用中，其实都是在SystemUI中显示的，是在另一个Window中。所以WindowInsets中的可以理解成手机屏幕这样一个视觉窗口。&lt;/p>
&lt;p>WindowInsets被设计出来就是要解决上面的这种场景的。你需要为接受软键盘高度变化的View设置一个listener，当软键盘变化时，就能从listener中拿到软键盘新的高度并做出对应处理。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">findViewById&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">setOnApplyWindowInsetsListener&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">insets&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">ime&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">insets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getInsets&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">WindowInsetsCompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Type&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ime&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">v&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">setPadding&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">left&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">top&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">right&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">bottom&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">insets&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>细心的你也许发现了WindowInsetsCompat.Type.ime()，Type中也是有其他的类型的。像是systemBars、systemGestures等等，详细的列表你可以在&lt;a href="https://developer.android.com/develop/ui/compose/layouts/insets?hl=zh-cn">在应用中全屏显示内容，并在 Compose 中处理窗口边衬区&lt;/a>中找到。&lt;/p>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;a href="https://developer.android.com/develop/ui/compose/layouts/insets?hl=zh-cn">在应用中全屏显示内容，并在 Compose 中处理窗口边衬区&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://developer.android.com/reference/android/view/WindowInsets">WindowInsets&lt;/a>&lt;/p></description></item><item><title>软路由安装OpenWrt</title><link>https://opoojkk.com/posts/2024/12/22/install-openwrt-on-x86-machine/</link><pubDate>Sun, 22 Dec 2024 07:51:53 +0800</pubDate><guid>https://opoojkk.com/posts/2024/12/22/install-openwrt-on-x86-machine/</guid><description>&lt;p>软路由机器是一台J4125的小主机，只作为路由器运行。常年运行esir的固件，也是用OpenClash科学上网。大概是DNS设置的有问题，只有在fake ip模式下打开TUN时才能访问网络，挨个查原因估计要更麻烦，索性直接重装。
用人编译好的固件总是不能满足需求，要么是有多余的插件，安装之后从来不打开，要么是安装一个使用频率很高的插件，却只能跟固件一起更新，综合考虑决定安装immortalWrt。好处是内核用原版OpenWrt的，只有核心的插件，其他的根据需要安装。&lt;/p>
&lt;p>我开始把系统装在了U盘上，还要再把系统从U盘上复制到硬盘，嫌麻烦。直接制作U盘启动器，装到主机的硬盘上。&lt;/p>
&lt;h2 class="heading" id="准备">
 准备&lt;span class="heading__anchor"> &lt;a href="#%e5%87%86%e5%a4%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>一台软路由&lt;/li>
&lt;li>一块U盘&lt;/li>
&lt;li>一台显示器和对应的线（HDMI或是VGA）&lt;/li>
&lt;li>鼠标&lt;/li>
&lt;li>键盘&lt;/li>
&lt;li>[winpe]工具(&lt;a href="https://www.wepe.com.cn/">https://www.wepe.com.cn/&lt;/a>)&lt;/li>
&lt;li>[physdiskwrite]工具(&lt;a href="https://m0n0.ch/wall/downloads/physdiskwrite-0.5.3.zip">https://m0n0.ch/wall/downloads/physdiskwrite-0.5.3.zip&lt;/a>)&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="制作启动盘">
 制作启动盘&lt;span class="heading__anchor"> &lt;a href="#%e5%88%b6%e4%bd%9c%e5%90%af%e5%8a%a8%e7%9b%98">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>下载winpe后选择插入的U盘制作启动盘，等待完成即可。
下载physdiskwrite，解压把exe文件放到U盘跟目录&lt;/p>
&lt;h2 class="heading" id="下载固件">
 下载固件&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%8b%e8%bd%bd%e5%9b%ba%e4%bb%b6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>访问&lt;a href="https://downloads.immortalwrt.org/">ImmortalWrt Downloads&lt;/a>或是&lt;a href="https://firmware-selector.immortalwrt.org/">ImmortalWrt Fireware Selector&lt;/a>，如果你用和我相同的CPU，那么你需要下载x86/generic/下的。&lt;/p>
&lt;p>拿J4125举例，链接就是https://downloads.immortalwrt.org/releases/23.05.4/targets/x86/generic/。列表中都是gz结尾的文件，和zip、txt的作用是一样的。gz表明文件gzip格式的。
下载完成后解压得到img文件，复制到U盘的根目录。&lt;/p>
&lt;p>制作启动盘用到的是img的镜像文件，你可以选择的只有img.gz结尾的文件。这些文件中差异有两个：&lt;/p>
&lt;ol>
&lt;li>efi&lt;/li>
&lt;li>ext4和squashfs&lt;/li>
&lt;/ol>
&lt;h3 class="heading" id="efi">
 efi&lt;span class="heading__anchor"> &lt;a href="#efi">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>对应引导方式，新的主板都是支持UEFI启动的，选择两种都可以，不动。&lt;/p>
&lt;h3 class="heading" id="squashfs">
 squashfs&lt;span class="heading__anchor"> &lt;a href="#squashfs">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>ext4是可修改的，另一个是只读的，比如，路由器的恢复出厂设置功能就依赖这个只读功能&lt;/p>
&lt;h2 class="heading" id="制作启动盘-1">
 制作启动盘&lt;span class="heading__anchor"> &lt;a href="#%e5%88%b6%e4%bd%9c%e5%90%af%e5%8a%a8%e7%9b%98-1">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>用img.gz尝试了很多次都失败了，用img立马通过。&lt;/p>
&lt;h3 class="heading" id="删除磁盘分区">
 删除磁盘分区&lt;span class="heading__anchor"> &lt;a href="#%e5%88%a0%e9%99%a4%e7%a3%81%e7%9b%98%e5%88%86%e5%8c%ba">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>打开桌面上的diskgenius，删除主机磁盘上的所有分区，删除后不要忘了点击左上角的保存。&lt;/p>
&lt;h3 class="heading" id="将u盘中的固件复制到硬盘">
 将U盘中的固件复制到硬盘&lt;span class="heading__anchor"> &lt;a href="#%e5%b0%86u%e7%9b%98%e4%b8%ad%e7%9a%84%e5%9b%ba%e4%bb%b6%e5%a4%8d%e5%88%b6%e5%88%b0%e7%a1%ac%e7%9b%98">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>在开始菜单中打开powershel，输入&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">physkiskwrite.exe -u $镜像文件路径
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>$镜像文件路径 替换成你要刷的镜像文件路径。也可以直接拖拽文件到终端得到&lt;/p>
&lt;p>显示finished即完成&lt;/p>
&lt;h2 class="heading" id="安装">
 安装&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%89%e8%a3%85">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>重启主机后狂按DEL键（一般是DEL或者F11，如果都不是搜下自己主机主板对应的按键是什么）。讲启动方式改为从U盘启动。开机等待终端能输入时也就完成了。这时候U盘就可以拔掉了。&lt;/p>
&lt;h2 class="heading" id="扩容">
 扩容&lt;span class="heading__anchor"> &lt;a href="#%e6%89%a9%e5%ae%b9">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>安装后可用空间只有100+M留给你安装插件，但凡装个什么就爆了。直接看eSir的&lt;a href="https://www.youtube.com/watch?v=YwbwzuXKNlg">教程&lt;/a>解释了原理，即使你不在意原理跟着操作也能完成。&lt;/p>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;a href="https://blog.csdn.net/qq_35379989/article/details/129460736">路由系列：软路由物理机安装openwrt 小白教程（esir高大全）&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://juejin.cn/post/7082633562695942157">Linux装机之EFI分区理解&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://www.right.com.cn/forum/thread-1425096-1-1.html">ext4-img.gz，squashfs.img.gz，rootfs.tar.gz等几个文件区别？&lt;/a>&lt;/p></description></item><item><title>View中避免使用Context参数</title><link>https://opoojkk.com/posts/2024/12/15/do-not-add-the-context-parameter-to-the-view/</link><pubDate>Sun, 15 Dec 2024 09:22:50 +0800</pubDate><guid>https://opoojkk.com/posts/2024/12/15/do-not-add-the-context-parameter-to-the-view/</guid><description>&lt;p>工作的这几年，看见过很多次这种写法，很不爽，想吐槽。&lt;/p>
&lt;p>明明View里就有个Context的变量，还要画蛇添足，再加个参数保存Context，这个Context很大概率是Activity。&lt;/p>
&lt;p>恐怕是忘了传入View里的这个Context就是Activity本人吧。&lt;/p>
&lt;p>来看下Activity设置内容View：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// AppCompatDelegateImpl.java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// Activity解析xml布局文件的代码&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">setContentView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">resId&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ensureSubDecor&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">contentParent&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mSubDecor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">findViewById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">id&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">content&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">contentParent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">removeAllViews&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">LayoutInflater&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mContext&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">inflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">contentParent&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mAppCompatWindowCallback&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">bypassOnContentChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mWindow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getCallback&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// LayoutInflater.java&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// getContext&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// mContext就是从Activity里传进来的兄弟&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getContext&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mContext&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// 中调用到的inflate方法&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">inflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@LayoutRes&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nd">@Nullable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Resources&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getContext&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getResources&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DEBUG&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">d&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;INFLATING from resource: \&amp;#34;&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getResourceName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;\&amp;#34; (&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Integer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">toHexString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;)&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tryInflatePrecompiled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">XmlResourceParser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">inflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">finally&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">close&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// 再往下走&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nd">@Nullable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">tryInflatePrecompiled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@LayoutRes&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Resources&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nd">@Nullable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Try to inflate using a precompiled layout.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pkg&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getResourcePackageName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">layout&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getResourceEntryName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Class&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">clazz&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Class&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">forName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pkg&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;.CompiledView&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mPrecompiledClassLoader&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Method&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">inflater&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">clazz&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getMethod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">layout&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">inflater&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mContext&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// We were able to use the precompiled inflater, but now we need to do some work to&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// attach the view to the root correctly.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">XmlResourceParser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">AttributeSet&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Xml&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">asAttributeSet&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">advanceToRootNode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LayoutParams&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">generateLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">finally&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">close&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Throwable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DEBUG&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">e&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Failed to use precompiled view&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">finally&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Trace&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">traceEnd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Trace&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">TRACE_TAG_VIEW&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>都写明白了&lt;code>View view = (View) inflater.invoke(null, mContext, resource);&lt;/code>传入的还是他。&lt;/p>
&lt;p>不明白🏳️。&lt;/p></description></item><item><title>Ubuntu环境AOSP编译指南</title><link>https://opoojkk.com/posts/2024/12/15/compile-aosp-on-ubuntu/</link><pubDate>Sun, 15 Dec 2024 08:08:23 +0800</pubDate><guid>https://opoojkk.com/posts/2024/12/15/compile-aosp-on-ubuntu/</guid><description>&lt;h2 class="heading" id="起因">
 起因&lt;span class="heading__anchor"> &lt;a href="#%e8%b5%b7%e5%9b%a0">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>网上有这么多相似的文章，还要再写这个的原因有两个：&lt;/p>
&lt;ol>
&lt;li>纸上得来终觉浅。从去年就尝试按着网上的文章编译，总也不能通过，各种各样的问题；&lt;/li>
&lt;li>记录一下过程，避免踩相同的坑，也好给其他的朋友一点参考。&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="准备">
 准备&lt;span class="heading__anchor"> &lt;a href="#%e5%87%86%e5%a4%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="环境">
 环境&lt;span class="heading__anchor"> &lt;a href="#%e7%8e%af%e5%a2%83">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>先后尝试过用Windows、Mac编译，推荐直接用Linux，不管是不是Ubuntu。&lt;/p>
&lt;p>Windows的好处是方便拓展，硬盘相对便宜，日常使用我相信不是Mac就是Windows了，Windows的装机量绝对是最多的。因此开始打算在Windows上编译。为了能在Windows上用shell命令，装了一堆东西，各种方案都试过，最后还是无果费了一顿劲。&lt;/p>
&lt;p>Mac的是类Unix，有现成的shell可以用，电脑配置Macbook Pro 14&amp;rsquo; 16G内存+512G硬盘。结论是硬盘不够😅。&lt;/p>
&lt;p>用Windows还是Mac都是为了偷点懒，为了省这一小点的时间，反而浪费了更多时间，得不偿失。&lt;/p>
&lt;p>因此，我建议直接Linux开干。&lt;/p>
&lt;p>如果你有Windows，我建议装个虚拟机，Vmware Workstation Pro对个人都免费了，Linux镜像也是一搜一大把。（直接Google搜索，害怕百度粘上脏东西）。&lt;/p>
&lt;h3 class="heading" id="装备">
 装备&lt;span class="heading__anchor"> &lt;a href="#%e8%a3%85%e5%a4%87">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>硬件：PC电脑
系统：Window 10
内存：32G
硬盘：2T
虚拟机软件：VMware Workstation Pro
Linux：Ubuntu 22-04.5&lt;/p>
&lt;p>我会把用到的软件和镜像上传到Google Drive，省去大家自己找的功夫，不放心也可以去官方找，都是免费的。只有VMware下载稍麻烦些。&lt;/p>
&lt;p>Linux用你习惯即可。&lt;/p>
&lt;p>我编译的是AOSP 13 r44的版本，建议为虚拟机分配硬盘时分配400G硬盘空间，开始分配300G同步代码完成后磁盘满了，很尴尬。&lt;/p>
&lt;h3 class="heading" id="编译环境">
 编译环境&lt;span class="heading__anchor"> &lt;a href="#%e7%bc%96%e8%af%91%e7%8e%af%e5%a2%83">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>创建虚拟机相信聪明的你可以搞定。创建后开搞。&lt;/p>
&lt;h4 class="heading" id="jdk">
 JDK&lt;span class="heading__anchor"> &lt;a href="#jdk">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 安装jdk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt install openjdk-11-jdk
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 class="heading" id="python3">
 Python3&lt;span class="heading__anchor"> &lt;a href="#python3">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>Ubuntu 自 16.04 版本已经默认预装 python3，repo同步时用的命令都是python&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 通过软链接将python命令默认指向python3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo ln -s /usr/bin/python3 /usr/bin/python
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 class="heading" id="安装依赖工具和库">
 安装依赖工具和库&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%89%e8%a3%85%e4%be%9d%e8%b5%96%e5%b7%a5%e5%85%b7%e5%92%8c%e5%ba%93">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 class="heading" id="配置git信息">
 配置git信息&lt;span class="heading__anchor"> &lt;a href="#%e9%85%8d%e7%bd%aegit%e4%bf%a1%e6%81%af">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 配置Git的用户名&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git config --global user.name &lt;span class="s2">&amp;#34;Your Name&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 配置邮箱&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git config --global user.email you@example.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 class="heading" id="安装repo">
 安装Repo&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%89%e8%a3%85repo">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>Repo 是 Google 专门用来管理 Android 大型源码库的一个工具，比如 AOSP 项目。
简单说就是一个封装了 Git 命令的 python 脚本，用以简化跨多个 Git 仓库的代码管理和同步。通过 Repo，你可以在一个命令行中对所有仓库进行同步、检出特定的分支、以及执行其他 Git 操作。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 安装repo&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt install repo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看repo版本(是否安装成功)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">repo version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="编译">
 编译&lt;span class="heading__anchor"> &lt;a href="#%e7%bc%96%e8%af%91">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="创建aosp文件夹">
 创建AOSP文件夹&lt;span class="heading__anchor"> &lt;a href="#%e5%88%9b%e5%bb%baaosp%e6%96%87%e4%bb%b6%e5%a4%b9">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>通过repo拉取。先创建一个文件夹。&lt;/p>
&lt;p>以我的情况为例，我希望把文件夹命名清楚，以免过段时间字再想看时忘了是哪个版本了。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建文件夹&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkdir AOSP_13_r44
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 进入AOSP_13_r44&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> AOSP_13_r44
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="初始化repo">
 初始化repo&lt;span class="heading__anchor"> &lt;a href="#%e5%88%9d%e5%a7%8b%e5%8c%96repo">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 从Google下载Android13源码&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r44
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 从镜像站下载android-13.0.0_r44源码&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-13.0.0_r44
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>虽然VPN是程序员必备的工具，但不一定有那么多流量，有那么多流量也不一定速度快，速度快也不一定稳定，建议从镜像站下载。&lt;/p>
&lt;p>我用的清华的源（和上面相同），你也可以用中科大的，可以看下&lt;a href="https://androidperformance.com/2021/10/26/build-android-12/">高爷的文章&lt;/a>&lt;/p>
&lt;p>这里说的是后面同步代码的，repo初始化时你需要&lt;strong>打开VPN&lt;/strong>，让repo顺利完成初始化工作。&lt;/p>
&lt;h3 class="heading" id="拉取代码">
 拉取代码&lt;span class="heading__anchor"> &lt;a href="#%e6%8b%89%e5%8f%96%e4%bb%a3%e7%a0%81">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>拉取时我劝你关掉VPN，流量直接走直连是最稳定的。开始时我设置路由器上的openclash模式为TUN，总是TLS握手失败，重试了好多次都还是不行。直连从来没有失败过。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 默认命令&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">repo sync
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 只拉取当前分支&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">repo sync -c
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>只拉取当前分支会快一些，核心的代码改动很小，拉一个分支就足够了。&lt;/p>
&lt;p>命令解释：
-c current branch
&amp;ndash;no-tags 不拉取tags（默认）
-j 并行job数量，默认是0，根据CPU核心线程决定&lt;/p>
&lt;p>更多命令敲&lt;code>repo sync --h&lt;/code>查看。&lt;/p>
&lt;p>同步开始后是漫长的等待直到提示&lt;code>repo sync has finished successfully.&lt;/code>就算完成了。&lt;/p>
&lt;h3 class="heading" id="开始编译">
 开始编译&lt;span class="heading__anchor"> &lt;a href="#%e5%bc%80%e5%a7%8b%e7%bc%96%e8%af%91">#&lt;/a>&lt;/span>
&lt;/h3>&lt;h4 class="heading" id="初始化环境">
 初始化环境&lt;span class="heading__anchor"> &lt;a href="#%e5%88%9d%e5%a7%8b%e5%8c%96%e7%8e%af%e5%a2%83">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># 执行该命令，envsetup.sh 脚本初始化环境
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">source build/envsetup.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>此脚本会导入若干命令（包括本页中使用的命令），让您能够使用 Android 源代码。如果虚拟机关机了，需要重新执行一次。&lt;/p>
&lt;h4 class="heading" id="选择构建目标">
 选择构建目标&lt;span class="heading__anchor"> &lt;a href="#%e9%80%89%e6%8b%a9%e6%9e%84%e5%bb%ba%e7%9b%ae%e6%a0%87">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>使用 lunch 命令选择要构建的目标&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 默认，从后续列表中选择构建目标，&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">lunch
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>你也可以选择你要构建的目标，直接输入。比如：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">lunch aosp_cf_x86_64_phone-trunk_staging-userdebug
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>构建目标中的每个字段代表什么含义推荐参考&lt;a href="https://source.android.com/docs/setup/build/building?hl=zh-cn#choose-a-target">官方文档&lt;/a>，内容很全面。&lt;/p>
&lt;h4 class="heading" id="构建代码">
 构建代码&lt;span class="heading__anchor"> &lt;a href="#%e6%9e%84%e5%bb%ba%e4%bb%a3%e7%a0%81">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>选择好构建的目标之后，开始执行。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">m
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>您应看到的第一个输出是目标和构建环境的概要：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="o">============================================&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PLATFORM_VERSION_CODENAME&lt;/span>&lt;span class="o">=&lt;/span>VanillaIceCream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PLATFORM_VERSION&lt;/span>&lt;span class="o">=&lt;/span>VanillaIceCream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PRODUCT_INCLUDE_TAGS&lt;/span>&lt;span class="o">=&lt;/span>com.android.mainline
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">TARGET_PRODUCT&lt;/span>&lt;span class="o">=&lt;/span>aosp_arm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">TARGET_BUILD_VARIANT&lt;/span>&lt;span class="o">=&lt;/span>eng
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">TARGET_ARCH&lt;/span>&lt;span class="o">=&lt;/span>arm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">TARGET_ARCH_VARIANT&lt;/span>&lt;span class="o">=&lt;/span>armv7-a-neon
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">TARGET_CPU_VARIANT&lt;/span>&lt;span class="o">=&lt;/span>generic
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">HOST_OS&lt;/span>&lt;span class="o">=&lt;/span>linux
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">HOST_OS_EXTRA&lt;/span>&lt;span class="o">=&lt;/span>Linux-6.5.13-1rodete2-amd64-x86_64-Debian-GNU/Linux-rodete
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">HOST_CROSS_OS&lt;/span>&lt;span class="o">=&lt;/span>windows
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">BUILD_ID&lt;/span>&lt;span class="o">=&lt;/span>AOSP.MAIN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OUT_DIR&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">out&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">============================================&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 class="heading" id="设置swap">
 设置swap&lt;span class="heading__anchor"> &lt;a href="#%e8%ae%be%e7%bd%aeswap">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>如果编译过程中没有问题，等待完成即可。我给虚拟机的内容是16G，中间失败了一次。&lt;/p>
&lt;p>报错信息：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">soong bootstrap failed with: &lt;span class="nb">exit&lt;/span> status &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>搜索后是因为内容不够，swap又小。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-plain" data-lang="plain">&lt;span class="line">&lt;span class="cl">I found the following video very interesting and a straightforward approach to increase the size of swapfile. Checkout this short video tutorial: How to Increase Swap on Ubuntu Linux | Linux Handbook
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(From this video) following is the way to increase the swap space (if your system is using swapfile):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">First check the swap size of your linux system:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">swapon --show
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Sample output:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NAME TYPE SIZE USED PRIO
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/swapfile file 797.2M 0B -2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">This output shows the swap size is being used and the type of swap. Here the swap type is &amp;#34;file&amp;#34; type (it could be partition type as well).Ubuntu uses swapfile by default and increasing and decreasing the size of it is fairly easy.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">As we are going to modify the swapfile so at first turn it OFF. You might notice the process is being killed for multiple times. This happens because before turning the swap OFF the system moves its contents to main memory which may overflow the main memory. So, if the following command is killed, run it multiple times until it is successfully completed. You may also face sudden logout and / or other processes may be killed automatically.:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo swapoff /swapfile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Now if you run swapon --show it won&amp;#39;t show anything as it is turned OFF.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Now change the swap size (I am making it 4GB, for n GB make it nG):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo fallocate -l 4G /swapfile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Now if you check the file you will see the size is increased. To see it:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ls -lh /swapfile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Now tell the Linux system to use this file as swap system:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo mkswap /swapfile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">This will wipe the old swap signature and use the new one.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Now to turn the swap ON (as we initially turned OFF the swap space):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo swapon /swapfile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Done!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Now use swapon --show or free -h to see the change.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>来自&lt;a href="https://askubuntu.com/a/1263160">How to increase swap space?&lt;/a>&lt;/p>
&lt;p>我分配的16G😂。&lt;/p>
&lt;h2 class="heading" id="打开">
 打开&lt;span class="heading__anchor"> &lt;a href="#%e6%89%93%e5%bc%80">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>不出意外等编译完成后，用Android Studio for platfom打开，打开时输入构建target和希望打开的模块，大部分代码都在framework里。&lt;/p>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;a href="https://kdsunset.top/ubaosp13/index.html">Ubuntu 下编译 AOSP 源码&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://androidperformance.com/2021/10/26/build-android-12/">Android 系统开发系列（1）：Android 12 源代码下载、编译和刷机&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://askubuntu.com/a/1263160">How to increase swap space?&lt;/a>&lt;/p></description></item><item><title>Mac制作Windows启动盘指南</title><link>https://opoojkk.com/posts/2024/12/14/creating-a-windows-bootable-drive-on-mac/</link><pubDate>Sat, 14 Dec 2024 04:51:03 +0800</pubDate><guid>https://opoojkk.com/posts/2024/12/14/creating-a-windows-bootable-drive-on-mac/</guid><description>&lt;p>周二早上想看硬件有哪些，记得Windows电脑上有个AIDE，费了一顿劲找着，点了电脑直接蓝屏了，再进发现UEFI干没了😅。刚好手头又没有pe启动盘，倒是有U盘和Mac电脑，记录下用Mac制作Windows启动盘的过程。&lt;/p>
&lt;h2 class="heading" id="工具">
 工具&lt;span class="heading__anchor"> &lt;a href="#%e5%b7%a5%e5%85%b7">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>你需要准备的工具：
0. 一台Windows电脑；&lt;/p>
&lt;ol>
&lt;li>一个U盘，最小8G的都行；&lt;/li>
&lt;li>一台Mac电脑；&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="下载iso镜像文件">
 下载ISO镜像文件&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%8b%e8%bd%bdiso%e9%95%9c%e5%83%8f%e6%96%87%e4%bb%b6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>我建议去官网下载，主要有三个理由：&lt;/p>
&lt;ol>
&lt;li>官方没有各种牛皮藓广告，不要求你一定要登录才能下载；&lt;/li>
&lt;li>系统纯净，最干净的系统镜像，其他来源的不能保证；&lt;/li>
&lt;li>下载速度快，好歹也是官方的。&lt;/li>
&lt;/ol>
&lt;p>链接：
windows10：&lt;a href="https://www.microsoft.com/zh-cn/software-download/windows10ISO">微软官网下载&lt;/a>
windows11：&lt;a href="https://www.microsoft.com/zh-cn/software-download/windows11">微软官网下载&lt;/a>&lt;/p>
&lt;p>逐步选择你要下载的版本、语言、32位还是64位（近几年的电脑无脑选64就可以了）开始下载，过程中需要点击几次确认。&lt;/p>
&lt;h2 class="heading" id="制作启动盘">
 制作启动盘&lt;span class="heading__anchor"> &lt;a href="#%e5%88%b6%e4%bd%9c%e5%90%af%e5%8a%a8%e7%9b%98">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="格式化u盘">
 格式化U盘&lt;span class="heading__anchor"> &lt;a href="#%e6%a0%bc%e5%bc%8f%e5%8c%96u%e7%9b%98">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>在Mac终端中输入&lt;code>diskutil list&lt;/code>，显示接入的磁盘，U盘的位置在最后，根据容量大小和名称判断是哪一个。
比如我的：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">/dev/disk6 &lt;span class="o">(&lt;/span>external, physical&lt;span class="o">)&lt;/span>:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">#: TYPE NAME SIZE IDENTIFIER&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0: GUID_partition_scheme *31.0 GB disk6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1: Apple_HFS Blank 209.7 MB disk6s1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2: Microsoft Basic Data THEONE 30.8 GB disk6s2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>找到了U盘后，格式化U盘。&lt;/p>
&lt;p>&lt;code>diskutil eraseDisk MS-DOS &amp;quot;$U盘名称&amp;quot; GPT $卷名&lt;/code>&lt;/p>
&lt;p>$U盘名称 -&amp;gt; 替换成你希望U盘格式化后的名称
$卷名 -&amp;gt; 替换成U盘对应的，我的就应该换成了/dev/disk6&lt;/p>
&lt;h3 class="heading" id="挂载iso文件">
 挂载ISO文件&lt;span class="heading__anchor"> &lt;a href="#%e6%8c%82%e8%bd%bdiso%e6%96%87%e4%bb%b6">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">hdiutil mount &lt;span class="nv">$ISO文件&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>$ISO文件替换成你的ISO文件所在的路径包括最后的iso文件后缀。&lt;/p>
&lt;p>有一个小技巧，你可以输入&lt;code>hdiutil mount &lt;/code>后把ISO文件拖拽到终端中由系统帮你输入路径。&lt;/p>
&lt;p>完成后终端会输出ISO挂载的路径类似&lt;code>/Volumes/CCCOMA_X64FRE_ZH-CN_DV9&lt;/code>&lt;/p>
&lt;h3 class="heading" id="将iso同步u盘">
 将ISO同步U盘&lt;span class="heading__anchor"> &lt;a href="#%e5%b0%86iso%e5%90%8c%e6%ad%a5u%e7%9b%98">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">rsync -vha --exclude&lt;span class="o">=&lt;/span>sources/install.wim &lt;span class="nv">$ISO镜像文件挂载卷名&lt;/span>/* /Volumes/&lt;span class="nv">$U盘名称&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="拆分文件">
 拆分文件&lt;span class="heading__anchor"> &lt;a href="#%e6%8b%86%e5%88%86%e6%96%87%e4%bb%b6">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>由于fat32单文件最大智能支持4G，ios超过了文件限制，使用wimlib拆分镜像&lt;/p>
&lt;p>如果你安装了brew，推荐用brew安装：&lt;code>brew install wimlib&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 拆分install.wim文件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">wimlib-imagex split &lt;span class="nv">$ISO挂载路径&lt;/span>/sources/install.wim /Volumes/WIN11/sources/install.swm &lt;span class="m">4000&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>将$ISO挂载路径替换成你的。&lt;/p>
&lt;h3 class="heading" id="重要">
 ❗重要&lt;span class="heading__anchor"> &lt;a href="#%e9%87%8d%e8%a6%81">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Window安装时只能有一个UEFI分区，上面的步骤会为U盘创建一个UEFI分区，安装时又不显示U盘的分区，一直提示错误安装不上。&lt;/p>
&lt;p>解决：
&lt;code>diskutil eraseVolume HFS+ $分区名称 $卷名&lt;/code>&lt;/p>
&lt;p>$分区名称 -&amp;gt; 随便输入一个英文的名称即可
$卷名 -&amp;gt; 可以通过&lt;code>diskutil list&lt;/code>查看，替换为U盘中TYPE为UEFI格式的分区标识（最右侧一列）&lt;/p>
&lt;p>这一步完成后准备工作就完成了，开始安装系统。&lt;/p>
&lt;h2 class="heading" id="安装系统">
 安装系统&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%89%e8%a3%85%e7%b3%bb%e7%bb%9f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Windows电脑按F1、F11或者DELETE键进入BIOS（启动方式参考主板说明说），启动方式将U盘设置最优先的。
重启Window电脑，删除Windows电脑硬盘中的UEFI分区后，一路下一步安装系统。&lt;/p>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;a href="https://juejin.cn/post/7346404753439506482">记一次mac制作win11引导盘并安装&lt;/a>
&lt;a href="https://juejin.cn/post/7289717829732745275">使用mac制作U盘启动盘并安装win10系统&lt;/a>&lt;/p></description></item><item><title>理解Kotlin协程</title><link>https://opoojkk.com/posts/2024/12/08/kotlin-corortines/</link><pubDate>Sun, 08 Dec 2024 12:38:47 +0800</pubDate><guid>https://opoojkk.com/posts/2024/12/08/kotlin-corortines/</guid><description>&lt;blockquote>
&lt;p>记下自己的一点浅薄的理解，想要更深入学习的朋友推荐阅读霍老的&lt;a href="https://www.bennyhuo.com/2019/03/31/kotlin-coroutines-00-forword/">破解 Kotlin 协程&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;h2 class="heading" id="使用">
 使用&lt;span class="heading__anchor"> &lt;a href="#%e4%bd%bf%e7%94%a8">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">CoroutineScope&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">launch&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">EmptyCoroutineContext&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineStart&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nc">CoroutineStart&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DEFAULT&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">block&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">suspend&lt;/span> &lt;span class="nc">CoroutineScope&lt;/span>&lt;span class="p">.()&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Unit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">):&lt;/span> &lt;span class="n">Job&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">newContext&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">newCoroutineContext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">coroutine&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">start&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isLazy&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LazyStandaloneCoroutine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">newContext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">block&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">StandaloneCoroutine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">newContext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">active&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">coroutine&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">start&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">start&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">coroutine&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">block&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">coroutine&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>启动时包含三个参数：&lt;/p>
&lt;ol>
&lt;li>协程上下文&lt;/li>
&lt;li>启动模式&lt;/li>
&lt;li>执行方法&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="协程上下文">
 协程上下文&lt;span class="heading__anchor"> &lt;a href="#%e5%8d%8f%e7%a8%8b%e4%b8%8a%e4%b8%8b%e6%96%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>CoroutineContext中定义了get、plus、minusKey方法。类似list。&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>CoroutineContext&lt;/th>
 &lt;th>List&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>get(Key)&lt;/td>
 &lt;td>get(Int)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>plus(CoroutineContext)&lt;/td>
 &lt;td>plus(List)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>minusKey(Key)&lt;/td>
 &lt;td>removeAt(Int)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>官方的注释也表明，这是一个set的接口类。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Persistent context for the coroutine. It is an indexed set of [Element] instances.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * An indexed set is a mix between a set and a map.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Every element in this set has a unique [Key].
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>不用在意礼CoroutineContext类中是否有存储链表组或是其他存储数据的结构，这就是一个set（开始是写数组，纠正一下），怎么存储蛮可以交给子类实现。&lt;/p>
&lt;/blockquote>
&lt;p>key&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">interface&lt;/span> &lt;span class="nc">Key&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">E&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Element&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>element&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">interface&lt;/span> &lt;span class="nc">Element&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * A key of this coroutine context element.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Key&lt;/span>&lt;span class="p">&amp;lt;*&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="k">operator&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">E&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nc">Element&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Key&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">E&lt;/span>&lt;span class="p">&amp;gt;):&lt;/span> &lt;span class="n">E&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Suppress&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;UNCHECKED_CAST&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">key&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">key&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">this&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">E&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">fold&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">initial&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">R&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">operation&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Element&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">R&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">R&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">operation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">initial&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">minusKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Key&lt;/span>&lt;span class="p">&amp;lt;*&amp;gt;):&lt;/span> &lt;span class="n">CoroutineContext&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">key&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">key&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">EmptyCoroutineContext&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">this&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>默认情况在，CoroutineContext用的是EmptyCoroutineContext。几个协程上下文想加，实际上用的就是CombinedContext了。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="err">‍‍```&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="err">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">internal&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">CombinedContext&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">left&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">element&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Element&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Serializable&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">E&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nc">Element&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Key&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">E&lt;/span>&lt;span class="p">&amp;gt;):&lt;/span> &lt;span class="n">E&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">var&lt;/span> &lt;span class="py">cur&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">this&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">true&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cur&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">element&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">let&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">it&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">next&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">cur&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">left&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">next&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">CombinedContext&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cur&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">next&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">next&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="c1">// 省略其他代码
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">‍‍```&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>可以在CombinedContext中看到，内部是类似链表的结构，一个是当前节点存储的值（Element），另一个是指向下一个节点的指针（left）。&lt;/p>
&lt;p>到这里CoroutineContext的结构也就清晰了。响应的，EmptyCoroutineContext中都是空的。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@SinceKotlin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;1.3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">object&lt;/span> &lt;span class="nc">EmptyCoroutineContext&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Serializable&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">serialVersionUID&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Long&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">readResolve&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">Any&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">EmptyCoroutineContext&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">E&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nc">Element&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Key&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">E&lt;/span>&lt;span class="p">&amp;gt;):&lt;/span> &lt;span class="n">E&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">fold&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">initial&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">R&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">operation&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Element&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">R&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">R&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">initial&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">plus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">CoroutineContext&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">context&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">minusKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Key&lt;/span>&lt;span class="p">&amp;lt;*&amp;gt;):&lt;/span> &lt;span class="n">CoroutineContext&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">this&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">hashCode&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">Int&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">toString&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">String&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;EmptyCoroutineContext&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>一个CoroutineContext+另一个CoroutineContext的写法很常见，实际上是CoroutineContext中定义了operator fun plus方法，可以用+调用。翻开源码可以注意到组合时总是要先把ContinuationInterceptor放到链表（CombinedContext）最前面的位置以保证最先执行。&lt;/p>
&lt;blockquote>
&lt;p>因为CombinedContext的结构是类似3&amp;lt;-2&amp;lt;-1的，因此最先被拿到的是实际上是末尾的1这个元素。&lt;/p>
&lt;/blockquote>
&lt;h2 class="heading" id="coroutinestart">
 CoroutineStart&lt;span class="heading__anchor"> &lt;a href="#coroutinestart">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Defines start options for coroutines builders.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * It is used in the `start` parameter of coroutine builder functions like
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * [launch][CoroutineScope.launch] and [async][CoroutineScope.async]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * to describe when and how the coroutine should be dispatched initially.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * This parameter only affects how the coroutine behaves until the code of its body starts executing.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * After that, cancellability and dispatching are defined by the behavior of the invoked suspending functions.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * The summary of coroutine start options is:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * - [DEFAULT] immediately schedules the coroutine for execution according to its context.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * - [LAZY] delays the moment of the initial dispatch until the result of the coroutine is needed.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * - [ATOMIC] prevents the coroutine from being cancelled before it starts, ensuring that its code will start
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * executing in any case.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * - [UNDISPATCHED] immediately executes the coroutine until its first suspension point _in the current thread_.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>决定以哪种策略执行任务。&lt;/p>
&lt;p>DEFAULT：默认，立即安排执行。如果执行前被取消，将不会被执行。&lt;/p>
&lt;p>LAZY：需要的时机执行。调用Job.join或是start。如果执行前被取消，将不会被执行。&lt;/p>
&lt;p>ATOMIC：与默认类似，如果执行前被取消，会继续执行。&lt;/p>
&lt;p>UNDISPATCHED：立即执行，直到第一个挂起点。&lt;/p>
&lt;p>详细策略说明参见代码注释。路径：kotlinx.coroutines.CoroutineStart&lt;/p>
&lt;h2 class="heading" id="拦截器">
 拦截器&lt;span class="heading__anchor"> &lt;a href="#%e6%8b%a6%e6%88%aa%e5%99%a8">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>拦截器是什么东西呢？&lt;/p>
&lt;p>归根到底也是CoroutineContext的一个子类，在他的基础上额外实现了ContinuationInterceptor接口中interceptContinuation方法。Kotlin中现成的拦截器可以一直顺到Dispatchers的Default、Main、Unconfined、IO上，&lt;/p>
&lt;p>除了上面几个常用的，你也可以自己定义拦截器。&lt;/p>
&lt;p>以Dispatchers.Main为例，最后创建的是一个HandlerContext，Android开发的朋友看这个名字就猜到了，没错，就是Handler。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">dispatch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">block&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Runnable&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">handler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">block&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cancelOnRejection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">block&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>再回到launch时，根据CoroutineStart的策略，创建好Continuation，开始执行拦截逻辑。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// kotlinx.coroutines.intrinsics.Cancelable.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Use this function to start coroutine in a cancellable way, so that it can be cancelled
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * while waiting to be dispatched.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@InternalCoroutinesApi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="err">(&lt;/span>&lt;span class="nf">suspend&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">startCoroutineCancellable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">completion&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Continuation&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;):&lt;/span> &lt;span class="n">Unit&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">runSafely&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">completion&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">createCoroutineUnintercepted&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">completion&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">intercepted&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">resumeCancellableWith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">Result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">success&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Unit&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// kotlin.coroutines.jvm.internal.ContinuationImpl.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">intercepted&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">Continuation&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Any&lt;/span>&lt;span class="p">?&amp;gt;&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">intercepted&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ContinuationInterceptor&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">interceptContinuation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">?:&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">also&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">intercepted&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">it&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这是是Dispatchers.Main的，中间绕了几弯，最终会执行到HandlerContext（基类是HandlerDispatcher）。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">dispatch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">block&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Runnable&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">handler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">block&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cancelOnRejection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">block&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>都已经Handler了，还有啥好说的🤷‍♂️。&lt;/p>
&lt;p>这是Dispatcher.Main可以切换线程的原因。&lt;/p>
&lt;h3 class="heading" id="continuation">
 Continuation&lt;span class="heading__anchor"> &lt;a href="#continuation">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Continuation又是什么呢？&lt;/p>
&lt;blockquote>
&lt;p>the fact of continuing or a thing that continues or follows from something else
繼續;持續;延續;繼續發生（或進行）之事;某事的後續結果（或影響）&lt;/p>
&lt;p>Cambridge Dictionary&lt;/p>
&lt;/blockquote>
&lt;p>按照CoroutineStart为Default找了半天，其实是封装了执行的一个类。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Creates unintercepted coroutine without receiver and with result type [T].
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * This function creates a new, fresh instance of suspendable computation every time it is invoked.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * The [completion] continuation is invoked when coroutine completes with result or exception.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * This function returns unintercepted continuation.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Invocation of `resume(Unit)` starts coroutine immediately in the invoker&amp;#39;s call stack without going through the
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * [ContinuationInterceptor] that might be present in the completion&amp;#39;s [CoroutineContext].
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * It is the invoker&amp;#39;s responsibility to ensure that a proper invocation context is established.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Note that [completion] of this function may get invoked in an arbitrary context.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * [Continuation.intercepted] can be used to acquire the intercepted continuation.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Invocation of `resume(Unit)` on intercepted continuation guarantees that execution of
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * both the coroutine and [completion] happens in the invocation context established by
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * [ContinuationInterceptor].
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Repeated invocation of any resume function on the resulting continuation corrupts the
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * state machine of the coroutine and may result in arbitrary behaviour or exception.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@SinceKotlin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;1.3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">actual&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="err">(&lt;/span>&lt;span class="nf">suspend&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">createCoroutineUnintercepted&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">completion&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Continuation&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">):&lt;/span> &lt;span class="n">Continuation&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Unit&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">probeCompletion&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">probeCoroutineCreated&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">completion&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">BaseContinuationImpl&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">probeCompletion&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">createCoroutineFromSuspendFunction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">probeCompletion&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">Function1&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Continuation&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">?&amp;gt;).&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>执行过程中传递的都是Continuation对象。&lt;/p>
&lt;p>async执行的流程大致相似，返回值是从await返回的，而不是launch方法。&lt;/p>
&lt;h2 class="heading" id="协程scope">
 协程Scope&lt;span class="heading__anchor"> &lt;a href="#%e5%8d%8f%e7%a8%8bscope">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>CoroutineScope是一个提供了协程上下文的接口。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// CoroutineScope.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">//CoroutineScope
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">public&lt;/span> &lt;span class="k">interface&lt;/span> &lt;span class="nc">CoroutineScope&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">coroutineContext&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// MainScope
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">MainScope&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">CoroutineScope&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">ContextScope&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SupervisorJob&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="nc">Dispatchers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Main&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ContextScope
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">internal&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">ContextScope&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineScope&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">coroutineContext&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">context&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// CoroutineScope is used intentionally for user-friendly representation
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">toString&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="n">String&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;CoroutineScope(coroutineContext=&lt;/span>&lt;span class="si">$coroutineContext&lt;/span>&lt;span class="s2">)&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// GlobalScope
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">public&lt;/span> &lt;span class="k">object&lt;/span> &lt;span class="nc">GlobalScope&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineScope&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Returns [EmptyCoroutineContext].
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">coroutineContext&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">get&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">EmptyCoroutineContext&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>LifecycleScope也没忘，LifecycleScope实际的实现类是LifecycleCoroutineScopeImpl。再原基础上增加Lifecycle以实现监听声明周期变化。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">internal&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">LifecycleCoroutineScopeImpl&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">lifecycle&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Lifecycle&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">coroutineContext&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CoroutineContext&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleCoroutineScope&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">LifecycleEventObserver&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">..&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fun&lt;/span> &lt;span class="nf">register&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">launch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">Dispatchers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Main&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">immediate&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">currentState&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">INITIALIZED&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">addObserver&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="nd">@LifecycleCoroutineScopeImpl&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">coroutineContext&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">cancel&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onStateChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleOwner&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">currentState&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DESTROYED&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">removeObserver&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">coroutineContext&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">cancel&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;a href="https://www.bennyhuo.com/2019/04/11/coroutine-dispatchers/">破解 Kotlin 协程（3）：协程调度篇&lt;/a>&lt;/p></description></item><item><title>FreshRSS API使用指南</title><link>https://opoojkk.com/posts/2024/12/05/freshrss-api/</link><pubDate>Thu, 05 Dec 2024 07:53:41 +0800</pubDate><guid>https://opoojkk.com/posts/2024/12/05/freshrss-api/</guid><description>&lt;p>不喜欢新闻客户端里带有政治倾向和无关紧要的娱乐新闻，诸如某某明星跌倒之类。日常都用rss阅读，可以阅读我常看的内容，FreshRSS又是可以自己部署的非常好用的服务，理所当然的用上了。&lt;/p>
&lt;p>最近组了一台主机，重新安装FreshRSS之后总是也在reeder（iOS平台RSS阅读软件）上配置不了，记录一下。&lt;/p>
&lt;p>Docker一种容器化技术，每一个容器都有一个端口，要通过ip:port的形式访问，互相之间是隔离的，当然也是有通讯的方式的。&lt;/p>
&lt;p>拿我的情况举例，我的ip是：192.168.1.10，端口是80，访问FreshRSS的地址就是192.168.1.10:80，更严谨的应该是加上http://192.168.1.10:80。&lt;/p>
&lt;p>FreshRSS也很贴心，设置-账户-API管理下有个url，点击进去会提示你访问api的url是多少。像是这样的：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">FreshRSS API endpoints
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Google Reader compatible API
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Your API address:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">http://192.168.1.10/api/greader.php
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Google Reader API configuration test:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">⚠️ WARN: Probable invalid base URL in ./data/config.php
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Fever compatible API
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Your API address:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">http://192.168.1.10/api/fever.php
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Fever API configuration test:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">⚠️ WARN: Probable invalid base URL in ./data/config.php
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">HowjsayDictionary.comCambridge DictionaryYouglish
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>但其实不是这样的。拿我的情况举例，我的ip是：192.168.1.10，端口是80，那么我的api地址就是http://192.168.1.10/api/greader.php。其实我在Docker中部署的，通过这个url是找不到的，实际的应该是http://192.168.1.10:80/api/greader.php。&lt;/p>
&lt;p>需要注意的是你需要把ip和端口改成你已经部署的FreshRSS的，而不是照抄我的。&lt;/p>
&lt;p>另外，登录时的账号与你登录FreshRSS是相同的，但是密码不是，密码你需要在FreshRSS的设置-账户-API管理下单独设置才可以。&lt;/p></description></item><item><title>Jetpack ViewModel实现原理</title><link>https://opoojkk.com/posts/2024/11/23/jetpack-viewmodel/</link><pubDate>Sat, 23 Nov 2024 09:08:41 +0800</pubDate><guid>https://opoojkk.com/posts/2024/11/23/jetpack-viewmodel/</guid><description>&lt;p>ViewModel和架构MVVM中的VM（ViewModel）同名，但并不是一个东西，架构中的ViewModel是和View双向绑定的，这里的ViewModel只能做到利用LiveData更新。&lt;/p>
&lt;p>为什么用ViewModel呢？一是把网络请求和数据库读取的逻辑从Activity中放到了ViweModel，分担了一部分Activity的压力；另一个原因是可以在Activity或Fragment重建时保存数据，不会丢失。今天探讨的时第二个问题。&lt;/p>
&lt;h2 class="heading" id="用法">
 用法&lt;span class="heading__anchor"> &lt;a href="#%e7%94%a8%e6%b3%95">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>一般的创建写法是&lt;code>val viewModel :MyViewModel by viewModels()&lt;/code>这样，或者直接的写法应该是&lt;code>val viewModel: MyViewModel = ViewModelProvider(viewModelStore,defaultViewModelProviderFactory).get(MyViewModel::class.java)&lt;/code>这样才对。&lt;/p>
&lt;p>&lt;code>viewModels()&lt;/code>需要添加了activity-ktx的依赖，顺着代码往下翻实际上也是用了第二种，不同的是被Lazy包了一下。&lt;/p>
&lt;p>ViewModelProvider的构造方法需要三个参数分别是ViewModelStore，Factory和CreationExtras。&lt;/p>
&lt;p>ViewModelStore中有&lt;code>mutableMapOf&amp;lt;String, ViewModel&amp;gt;()&lt;/code>类型的属性，实际作用也是以kv的形式存放ViewModel。&lt;/p>
&lt;p>Factory更直接，一个工厂模式，决定怎么创建ViewModel类。&lt;/p>
&lt;p>CreationExtras中又是一个map：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">internal&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">map&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">MutableMap&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Key&lt;/span>&lt;span class="p">&amp;lt;*&amp;gt;,&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">?&amp;gt;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">mutableMapOf&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>保存额外的参数。&lt;/p>
&lt;h2 class="heading" id="原理">
 原理&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>保证ViewModel不被重建的奥秘藏在get方法中：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * an activity), associated with this `ViewModelProvider`.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * The created ViewModel is associated with the given scope and will be retained
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * as long as the scope is alive (e.g. if it is an activity, until it is
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * finished or process is killed).
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param modelClass The class of the ViewModel to create an instance of it if it is not
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * present.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @return A ViewModel that is an instance of the given type `T`.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @throws IllegalArgumentException if the given [modelClass] is local or anonymous class.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@MainThread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">open&lt;/span> &lt;span class="k">operator&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">T&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nc">ViewModel&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">modelClass&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Class&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;):&lt;/span> &lt;span class="n">T&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">canonicalName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">modelClass&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">canonicalName&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?:&lt;/span> &lt;span class="k">throw&lt;/span> &lt;span class="n">IllegalArgumentException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Local and anonymous classes can not be ViewModels&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">$DEFAULT&lt;/span>&lt;span class="s2">_KEY:&lt;/span>&lt;span class="si">$canonicalName&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">modelClass&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * an activity), associated with this `ViewModelProvider`.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * The created ViewModel is associated with the given scope and will be retained
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * as long as the scope is alive (e.g. if it is an activity, until it is
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * finished or process is killed).
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param key The key to use to identify the ViewModel.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param modelClass The class of the ViewModel to create an instance of it if it is not
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * present.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @return A ViewModel that is an instance of the given type `T`.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Suppress&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;UNCHECKED_CAST&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@MainThread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">open&lt;/span> &lt;span class="k">operator&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">T&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nc">ViewModel&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nf">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">String&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">modelClass&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Class&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;):&lt;/span> &lt;span class="n">T&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">viewModel&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">store&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">modelClass&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isInstance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">viewModel&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="n">factory&lt;/span> &lt;span class="k">as&lt;/span>&lt;span class="p">?&lt;/span> &lt;span class="n">OnRequeryFactory&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">onRequery&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">viewModel&lt;/span>&lt;span class="o">!!&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">viewModel&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">T&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Suppress&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;ControlFlowWithEmptyBody&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">viewModel&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// TODO: log a warning.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">extras&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">MutableCreationExtras&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">defaultCreationExtras&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">extras&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">VIEW_MODEL_KEY&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">key&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// AGP has some desugaring issues associated with compileOnly dependencies so we need to
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// fall back to the other create method to keep from crashing.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">factory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">modelClass&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">extras&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">AbstractMethodError&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">factory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">modelClass&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}.&lt;/span>&lt;span class="n">also&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">store&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">it&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>get时先尝试从store中获取，store也就是刚刚说的ViewModelStore。拿不到时才会用Factory创建一个新的。&lt;/p>
&lt;p>在ComponentActivity中取到的viewModelStore又是从一个NonConfigurationInstances类实例中拿到的，实例并不是Activity内部创建的，是在Activity#attach时传入并保存下来的（你不信我也没办法，赋值的代码只有attach有）。也就是说外部一定有对其赋值的代码。&lt;/p>
&lt;p>恰好知道Activity#attach方法在ActivityThread中用到。顺藤摸瓜，又找到是在Activity销毁前保存下来的。接着往上摸，找到handleRelaunchActivity的头上，确定在需要重新launch的Activity中才会传入NonConfigurationInstances，performLaunchActivity时传入null。&lt;/p>
&lt;p>知道了从哪进来的，从哪出去的就好说了。Activity重建时，会用retainNonConfigurationInstances拿到，其中一部分保存的是Activity的状态，也就是activity字段中。&lt;/p>
&lt;p>保存的activity名字叫activity，类型是Object的。实际的是NonConfigurationInstances，里面包含了viewModelStore。&lt;/p>
&lt;h2 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>获取ViewModel时如果是Activity重建的场景，会在销毁前将ViewModelStore保存下来，并将ViewModelStore传给Activity，ViewModelStore保存了ViewModel实例。这样Activity重建后仍然有之前的ViewModel。&lt;/p></description></item><item><title>关于旅行的意义</title><link>https://opoojkk.com/posts/2024/11/17/the-meaning-of-travel/</link><pubDate>Sun, 17 Nov 2024 12:15:14 +0800</pubDate><guid>https://opoojkk.com/posts/2024/11/17/the-meaning-of-travel/</guid><description>&lt;p>我总是想弄清楚各种意义，仿佛没有意义的事物就不应该存在。时常看到有人分享各地的标志性建筑、食物或是服饰等等，单独拿出来分享的还是少数，更多的还是与某些东西合影的照片。即使去旅行的时间非常短，但一定会有那么两三个带标志性的，标志性物品和某个城市、某个地区、某种文化划上了等号。&lt;/p>
&lt;p>对这些，我总是感到很厌恶，在我看来，这不是旅行。不过是走马观花，没有领略当地的风土人情，没有感受过这座城市中生活的人们特有的特性，也同样没有领略那里的文化，又怎么敢豪言去过那里呢。&lt;/p>
&lt;p>我出去玩时也是会拍照片的，拍了未必会上传到哪个平台上，好让人家看到我去了哪里，这种上传更多的是炫耀，一种岁月静好的错觉，而不是看到了美希望与人分享。没有觉得拍照片是什么问题，我甚至为了拍照片买了相机，有问题的是到了哪里总是先掏出手机拍个不停，摆拍出一张张照片，大有不拍出像样的照片就不走的气势，亦或是匆匆拍下两三张照片千直奔下一个景点。&lt;/p>
&lt;p>这大概是我不愿意与人一起出行的原因，一起去了总也要一起逛逛，一逛就到了热门景点，大到建筑，小到移动餐车，总是急匆匆的。人也多，却也挡不住大家的热情，排在长龙的队尾也不肯放弃。如果是我自己，完全可以放缓些，随意在街上走一走，累了饿了找家店，吃顿饭，吃饱喝足了再接着去哪里。&lt;/p></description></item><item><title>Jetpack Lifecycle组件解析</title><link>https://opoojkk.com/posts/2024/11/17/jetpack-lifecycle/</link><pubDate>Sun, 17 Nov 2024 12:04:10 +0800</pubDate><guid>https://opoojkk.com/posts/2024/11/17/jetpack-lifecycle/</guid><description>&lt;p>Android中又越来越多组件依赖Lifecycle，从来不好奇Lifecycle是什么东西，又是怎么起作用的吗？我很好奇。&lt;/p>
&lt;h2 class="heading" id="lifecycleowner">
 LifecycleOwner&lt;span class="heading__anchor"> &lt;a href="#lifecycleowner">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>通常的用法往往是从LifecyclerOwner中拿到Lifecycle对象，之后add Oberver或者remove Observer。&lt;/p>
&lt;p>LifecycleOwner是一个接口，只有一个参数（说是方法更确切）：Lifecycle。单纯的为了拿到Lifecycle对象。&lt;/p>
&lt;p>FragmentActivity和Fragment都继承自LifecycleOwner。内部又都持有一个LifecycleRegistry对象。&lt;/p>
&lt;p>我在想为什么需要这么一个方法呢？
如果没有这个方法会怎么呢？如果没有，每个持有Lifecycle的类中都需要保存Lifecycle，这是少不了的。获取就不一样了，怎么获取呢？每个类没有统一的方法啊。这么想LifecycleOwner就是统一的方法。对外只暴露拿到Lifecycle的方法。&lt;/p>
&lt;h2 class="heading" id="lifecycle">
 Lifecycle&lt;span class="heading__anchor"> &lt;a href="#lifecycle">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>LifecycleRegistry是Lifecycle对应的唯一实现，内部实现了记录Observer数量、添加、移除和改变状态的实际逻辑，不一一列举。&lt;/p>
&lt;p>Lifecycle是个抽象类，方法有两个：addObserver和removeObserver。参数都是一个LifecycleObserver示例。也不太需要解释，就是添加和移除Observer。&lt;/p>
&lt;p>此外还有一个State类型成员变量（可以理解成返回State类型的方法），保存当前的状态。&lt;/p>
&lt;h3 class="heading" id="state">
 State&lt;span class="heading__anchor"> &lt;a href="#state">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Lifecycle states. You can consider the states as the nodes in a graph and
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * [Event]s as the edges between these nodes.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">enum&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">State&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * any more events. For instance, for an [android.app.Activity], this state is reached
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * **right before** Activity&amp;#39;s [onDestroy][android.app.Activity.onDestroy] call.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DESTROYED&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Initialized state for a LifecycleOwner. For an [android.app.Activity], this is
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * the state when it is constructed but has not received
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * [onCreate][android.app.Activity.onCreate] yet.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">INITIALIZED&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Created state for a LifecycleOwner. For an [android.app.Activity], this state
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * is reached in two cases:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * * after [onCreate][android.app.Activity.onCreate] call;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * * **right before** [onStop][android.app.Activity.onStop] call.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CREATED&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Started state for a LifecycleOwner. For an [android.app.Activity], this state
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * is reached in two cases:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * * after [onStart][android.app.Activity.onStart] call;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * * **right before** [onPause][android.app.Activity.onPause] call.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">STARTED&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Resumed state for a LifecycleOwner. For an [android.app.Activity], this state
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * is reached after [onResume][android.app.Activity.onResume] is called.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">RESUMED&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Compares if this State is greater or equal to the given `state`.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param state State to compare with
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @return true if this State is greater or equal to the given `state`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">isAtLeast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">State&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">Boolean&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">compareTo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="lifecycleobserver">
 LifecycleObserver&lt;span class="heading__anchor"> &lt;a href="#lifecycleobserver">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>LifecycleObserver是一个接口（废话），单纯的是一个接口。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Marks a class as a LifecycleObserver. Don&amp;#39;t use this interface directly. Instead implement either
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * [DefaultLifecycleObserver] or [LifecycleEventObserver] to be notified about
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * lifecycle events.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @see Lifecycle Lifecycle - for samples and usage patterns.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">interface&lt;/span> &lt;span class="nc">LifecycleObserver&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>代码就是这么定义的。&lt;/p>
&lt;p>对应的实现接口有两个：LifecycleEventObserver和DefaultLifecycleObserver。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Class that can receive any lifecycle change and dispatch it to the receiver.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * If a class implements both this interface and
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * [androidx.lifecycle.DefaultLifecycleObserver], then
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * methods of `DefaultLifecycleObserver` will be called first, and then followed by the call
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * of [LifecycleEventObserver.onStateChanged]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * If a class implements this interface and in the same time uses [OnLifecycleEvent], then
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * annotations will be ignored.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">interface&lt;/span> &lt;span class="n">LifecycleEventObserver&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleObserver&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Called when a state transition event happens.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param source The source of the event
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param event The event
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onStateChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleOwner&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Event&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">interface&lt;/span> &lt;span class="nc">DefaultLifecycleObserver&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleObserver&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Notifies that `ON_CREATE` event occurred.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * This method will be called after the [LifecycleOwner]&amp;#39;s `onCreate`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * method returns.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param owner the component, whose state was changed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleOwner&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Notifies that `ON_START` event occurred.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * This method will be called after the [LifecycleOwner]&amp;#39;s `onStart` method returns.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param owner the component, whose state was changed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onStart&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleOwner&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Notifies that `ON_RESUME` event occurred.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * This method will be called after the [LifecycleOwner]&amp;#39;s `onResume`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * method returns.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param owner the component, whose state was changed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onResume&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleOwner&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Notifies that `ON_PAUSE` event occurred.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * This method will be called before the [LifecycleOwner]&amp;#39;s `onPause` method
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * is called.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param owner the component, whose state was changed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onPause&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleOwner&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Notifies that `ON_STOP` event occurred.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * This method will be called before the [LifecycleOwner]&amp;#39;s `onStop` method
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * is called.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param owner the component, whose state was changed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onStop&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleOwner&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Notifies that `ON_DESTROY` event occurred.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * This method will be called before the [LifecycleOwner]&amp;#39;s `onDestroy` method
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * is called.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param owner the component, whose state was changed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onDestroy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleOwner&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>两者的差别在提供的颗粒度不一样，一个是把每一个状态都提供了方法；另一个则是只提供了变化的callback，至于怎么用完全不关心。&lt;/p>
&lt;h2 class="heading" id="先后顺序">
 先后顺序&lt;span class="heading__anchor"> &lt;a href="#%e5%85%88%e5%90%8e%e9%a1%ba%e5%ba%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>接口LifecycleEventObserver的注释中表明如果同时实现两个接口，会先调用DefaultLifecycleObserver中的方法，然后再调用LifecycleEventObserver的方法。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">internal&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">DefaultLifecycleObserverAdapter&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">defaultLifecycleObserver&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">DefaultLifecycleObserver&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">val&lt;/span> &lt;span class="py">lifecycleEventObserver&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleEventObserver&lt;/span>&lt;span class="p">?&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleEventObserver&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onStateChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">LifecycleOwner&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">when&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ON_CREATE&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">defaultLifecycleObserver&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">onCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ON_START&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">defaultLifecycleObserver&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">onStart&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ON_RESUME&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">defaultLifecycleObserver&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">onResume&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ON_PAUSE&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">defaultLifecycleObserver&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">onPause&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ON_STOP&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">defaultLifecycleObserver&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">onStop&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ON_DESTROY&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">defaultLifecycleObserver&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">onDestroy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ON_ANY&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="n">IllegalArgumentException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;ON_ANY must not been send by anybody&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lifecycleEventObserver&lt;/span>&lt;span class="o">?.&lt;/span>&lt;span class="n">onStateChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>规则和顺序很清晰，又是怎么从Observer到DefaultLifecycleObserverAdapter的呢？&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Lifecycling.kt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">public&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">lifecycleEventObserver&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">`object`&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="n">LifecycleEventObserver&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">isLifecycleEventObserver&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">`object`&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">LifecycleEventObserver&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">isDefaultLifecycleObserver&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">`object`&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="n">DefaultLifecycleObserver&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">isLifecycleEventObserver&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">isDefaultLifecycleObserver&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">DefaultLifecycleObserverAdapter&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">`object`&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">DefaultLifecycleObserver&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">`object`&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">LifecycleEventObserver&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">..&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>方法lifecycleEventObserver会在LifecycleRegistry添加Observer时被调用，所有的也都串起来了。&lt;/p>
&lt;h2 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>注：实线表示持有，虚线表示继承&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/JdxoftN.png" alt="来个图">&lt;/figure></description></item><item><title>Jetpack LiveData解析</title><link>https://opoojkk.com/posts/2024/11/10/jetpack-livedata/</link><pubDate>Sun, 10 Nov 2024 08:54:33 +0800</pubDate><guid>https://opoojkk.com/posts/2024/11/10/jetpack-livedata/</guid><description>&lt;p>Jetpack中最不陌生的大概就是LiveData了，LiveData的两个特点：和lifecycleOwner绑定且监听数据变化。用过的哪个不说好？&lt;/p>
&lt;h2 class="heading" id="流程">
 流程&lt;span class="heading__anchor"> &lt;a href="#%e6%b5%81%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>常用的LiveData的形式大概是这样的：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">mutableLiveData&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">MutableLiveData&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">&amp;gt;()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">mutableLiveData&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">observe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 更新值
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">mutableLiveData&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">postValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">..&lt;/span>&lt;span class="p">.)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">mutableLiveData&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">setValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">..&lt;/span>&lt;span class="p">.)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>今天写的是LiveData怎么上来就是MutableLiveData呢？&lt;/p>
&lt;p>MutableLiveData是LiveData子类，LiveData还是抽象类，拿来就能用的就是MutableLiveData了。&lt;/p>
&lt;p>监听时有两个方法，分别是&lt;code>public void observe(@NonNull LifecycleOwner owner, @NonNull Observer&amp;lt;? super T&amp;gt; observer)&lt;/code>和&lt;code>public void observeForever(@NonNull Observer&amp;lt;? super T&amp;gt; observer)&lt;/code>&lt;/p>
&lt;p>observe传入的LifecycleOwner参数用作监听生命周期变化，LifecycleOwner被销毁前移出观察者。不会因此造成npe问题。
observeForever则不在意谁是否被销毁，只要有会通知。&lt;/p>
&lt;p>虽然两个方法传入的Observer是一样的类型，但在执行添加观察者时，又封装了一层，添加了版本、活跃状态及方法。observe方法添加的是LifecycleBoundObserver类型对象，observeForever添加的则是AlwaysActiveObserver类型对象，两者的基类是ObserverWrapper。&lt;/p>
&lt;p>更新值从调用setValue或是postValue方法开始，postValue用于子线程中执行，通用handler的方式使得更新值的操作在主线程中执行，实际执行的还是setValue。&lt;/p>
&lt;p>直接看setValue吧。setValue之后，LiveData内部做了三步，分别是：&lt;/p>
&lt;ol start="0">
&lt;li>判断主线程&lt;/li>
&lt;li>更新version&lt;/li>
&lt;li>保存更新后的值&lt;/li>
&lt;li>通知observer&lt;/li>
&lt;/ol>
&lt;p>先看通知observer，方法中可以传入一个可选的ObserverWrapper类型参数，对应上面的基类，如果传入了，只分发给这一个观察者，否则分发给所有的观察者。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl"> &lt;span class="n">void&lt;/span> &lt;span class="n">dispatchingValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@Nullable&lt;/span> &lt;span class="n">ObserverWrapper&lt;/span> &lt;span class="n">initiator&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">mDispatchingValue&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mDispatchInvalidated&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mDispatchingValue&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">do&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mDispatchInvalidated&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">initiator&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">considerNotify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">initiator&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">initiator&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Iterator&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nc">Map&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Entry&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Observer&lt;/span>&lt;span class="p">&amp;lt;?&lt;/span> &lt;span class="k">super&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span> &lt;span class="n">ObserverWrapper&lt;/span>&lt;span class="p">&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">iterator&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mObservers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">iteratorWithAdditions&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="n">iterator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">hasNext&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">considerNotify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">iterator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">next&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">getValue&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">mDispatchInvalidated&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">mDispatchInvalidated&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mDispatchingValue&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>重点的是决定观察者是否执行的方法：considerNotify。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="n">void&lt;/span> &lt;span class="n">considerNotify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ObserverWrapper&lt;/span> &lt;span class="n">observer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">mActive&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Check latest state b4 dispatch. Maybe it changed state but we didn&amp;#39;t get the event yet.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">//
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// we still first check observer.active to keep it as the entrance for events. So even if
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// the observer moved to an active state, if we&amp;#39;ve not received that event, we better not
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// notify for a more predictable notification order.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">shouldBeActive&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">activeStateChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">mLastVersion&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="n">mVersion&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">mLastVersion&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">mVersion&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">mObserver&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">onChanged&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">mData&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>有几个值得关注的点：&lt;/p>
&lt;ol>
&lt;li>mActive&lt;/li>
&lt;li>mVersion&lt;/li>
&lt;/ol>
&lt;p>只有状态是active时才该ObserverWrapper对象才可以通知观察者值变化了，状态的更新和获取都是和ObserverWrapper具体的实现类有关的，是哪一种类型又取决于通过哪种方式添加的观察者。&lt;/p>
&lt;p>mVersion的值也需要是最新的，都很合理。&lt;/p>
&lt;p>这大概就是LiveData中最主要的方法和相关流程，不知道你是否对主线程、mActive和mVersion有疑问，我是有的，为什么需要这些呢？分别来假设一下这些值没有会怎么样。&lt;/p>
&lt;h3 class="heading" id="mactive">
 mActive&lt;span class="heading__anchor"> &lt;a href="#mactive">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>mActive起了什么作用？需要保证观察者处于活跃状态才能接收到值的变化。如果所有观察者总是活跃状态会有什么问题吗？我相信是没有的，LifecyclerOwner总能保证按时移出。&lt;/p>
&lt;h3 class="heading" id="mversion">
 mVersion&lt;span class="heading__anchor"> &lt;a href="#mversion">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>mVersion又起了什么作用呢？mVersion变化来源是从setValue开始的，每更新一次value值，version就会更新一次。当观察者满足条件执行了，观察者内部的版本就会更新到最新。这样就可以理解成观察者活跃时总要保持最新的状态。&lt;/p>
&lt;p>如果没有mVersion会发生什么呢？只要值变化就更新，即使是因为时序问题传过来一个过时的值。那这个值就是不能缺少的。&lt;/p>
&lt;h3 class="heading" id="主线程">
 主线程&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%bb%e7%ba%bf%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>为什么所有的更新需要在主线程呢？不在主线程行不行？我相信也是可以的。大不了赋值时加锁，需要更新ui时谁需要谁切换好了。&lt;/p>
&lt;h3 class="heading" id="数据倒灌">
 数据倒灌？？？&lt;span class="heading__anchor"> &lt;a href="#%e6%95%b0%e6%8d%ae%e5%80%92%e7%81%8c">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>问题的原因是在添加观察者时，会把最新的状态一并传过去，这时候观察者就要更新到最新的状态。网上的博客中有很多解决方式，今天不谈解决。&lt;/p>
&lt;p>有没有可能Google就是这么设计的呢？如果时这么设计的，意图就是为了保证最新的状态，那么主线程执行是不是也能说的通了，版本、活跃状态是不是也能说的通了？&lt;/p>
&lt;p>所以它就是不是一个问题，Google设想的场景就不是通知状态变化的，而是保证所有的观察者保持最新的状态。&lt;/p>
&lt;h2 class="heading" id="组成">
 组成&lt;span class="heading__anchor"> &lt;a href="#%e7%bb%84%e6%88%90">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>MutableLiveData的代码没有额外的实现：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">MutableLiveData&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">extends&lt;/span> &lt;span class="n">LiveData&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Creates a MutableLiveData initialized with the given {@code value}.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param value initial value
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="n">MutableLiveData&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">T&lt;/span> &lt;span class="k">value&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">super&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Creates a MutableLiveData with no value assigned to it.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="n">MutableLiveData&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">super&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="n">void&lt;/span> &lt;span class="n">postValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">T&lt;/span> &lt;span class="k">value&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">postValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="n">void&lt;/span> &lt;span class="n">setValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">T&lt;/span> &lt;span class="k">value&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">setValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>LiveData的实现除了MutableLiveData还有个MediatorLiveData，是MutableLiveData的子类。根据官方注释中的解释，作用是用来协调多个LiveData，&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@link LiveData} subclass which may observe other {@code LiveData} objects and react on
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@code OnChanged} events from them.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * This class correctly propagates its active/inactive states down to source {@code LiveData}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * objects.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Consider the following scenario: we have 2 instances of {@code LiveData}, let&amp;#39;s name them
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@code liveData1} and {@code liveData2}, and we want to merge their emissions in one object:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@code liveDataMerger}. Then, {@code liveData1} and {@code liveData2} will become sources for
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * the {@code MediatorLiveData liveDataMerger} and every time {@code onChanged} callback
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * is called for either of them, we set a new value in {@code liveDataMerger}.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;pre&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * LiveData&amp;amp;lt;Integer&amp;amp;gt; liveData1 = ...;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * LiveData&amp;amp;lt;Integer&amp;amp;gt; liveData2 = ...;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * MediatorLiveData&amp;amp;lt;Integer&amp;amp;gt; liveDataMerger = new MediatorLiveData&amp;amp;lt;&amp;amp;gt;();
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * liveDataMerger.addSource(liveData1, value -&amp;amp;gt; liveDataMerger.setValue(value));
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * liveDataMerger.addSource(liveData2, value -&amp;amp;gt; liveDataMerger.setValue(value));
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;/pre&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Let&amp;#39;s consider that we only want 10 values emitted by {@code liveData1}, to be
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * merged in the {@code liveDataMerger}. Then, after 10 values, we can stop listening to {@code
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * liveData1} and remove it as a source.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;pre&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * liveDataMerger.addSource(liveData1, new Observer&amp;amp;lt;Integer&amp;amp;gt;() {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * private int count = 1;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@literal @}Override public void onChanged(@Nullable Integer s) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * count++;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * liveDataMerger.setValue(s);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * if (count &amp;amp;gt; 10) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * liveDataMerger.removeSource(liveData1);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * });
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;/pre&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param &amp;lt;T&amp;gt; The type of data hold by this instance
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>LiveData的独生子是MutableLiveData，MutableLiveData的独生子是MediatorLiveData。两代单传！&lt;/p></description></item><item><title>理解Android屏幕适配和最小宽度</title><link>https://opoojkk.com/posts/2024/11/06/android-screen-adaptation/</link><pubDate>Wed, 06 Nov 2024 08:05:07 +0800</pubDate><guid>https://opoojkk.com/posts/2024/11/06/android-screen-adaptation/</guid><description>&lt;p>凡事总要问为什么。为什么需要屏幕适配？
众所周知，Android是一个较开发的系统，允许各家厂商定制修改AOSP，选用不同规格的硬件，包括CPU、内存、电池、传感器等等等等。当然屏幕也在其中。也因此，大家手上的手机屏幕大不相同，体现在屏幕的尺寸、分辨率上。&lt;/p>
&lt;h2 class="heading" id="屏幕尺寸">
 屏幕尺寸&lt;span class="heading__anchor"> &lt;a href="#%e5%b1%8f%e5%b9%95%e5%b0%ba%e5%af%b8">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>屏幕尺寸是指从屏幕左上角到右下角这条对角线的距离，单位是英寸（inch）。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/TZqOL7C.png" alt="屏幕尺寸和分辨率">&lt;/figure>&lt;h2 class="heading" id="px">
 px&lt;span class="heading__anchor"> &lt;a href="#px">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>如果你对图像有了解，px（pixel）是图像的基本单位，当一张图片放大到不能再放大之后，图像中最小的一个色块就是px。一个像素只能显示一个颜色，许许多多像素放在一起，从宏观上看就是有变化的图片。&lt;/p>
&lt;p>值得注意：px不具有固定的大小，只是一个显示的最小单位。&lt;/p>
&lt;h2 class="heading" id="分辨率">
 分辨率&lt;span class="heading__anchor"> &lt;a href="#%e5%88%86%e8%be%a8%e7%8e%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>前面说px是图像的最小单位，手机屏幕图像的最小单位当然也是px，屏幕上有多少个px呢？这就是分辨率。
通常用横竖有多少个px表述分辨率，比如：&lt;/p>
&lt;ul>
&lt;li>640x480&lt;/li>
&lt;li>1920x1080（1080P）&lt;/li>
&lt;li>2048x1152（2k）&lt;/li>
&lt;li>4096x2160（4k）&lt;/li>
&lt;/ul>
&lt;p>在上面的图片中也就是纵向有1920px，横向有1080px，共1920x1080个。&lt;/p>
&lt;p>分辨率多对屏幕适配有什么影响呢？
试想一下：有两个设备，一个设备A屏幕的分辨率是1280x720，而另一个设备B屏幕是1920x1080。假设我想写一个View，宽度占屏幕宽度的一半（短的一个边），都用px写布局时，一个是360px，另一个是540px，我需要写两套布局。&lt;/p>
&lt;h2 class="heading" id="dp">
 dp&lt;span class="heading__anchor"> &lt;a href="#dp">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>dp（Density-Independent Pixels）中文像素无关密度，表示一英寸的屏幕上有多少个像素，把分辨率和物理屏幕大小联系起来，类似其他的密度，不过是把计算密度的方式换成屏幕的。与px的计算方式：&lt;code>px = dp * (dpi / 160)&lt;/code>。&lt;/p>
&lt;p>dpi和160又是什么东西呢？dpi是像素密度，计算方式为：&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/VhDchqQ.png" alt="ppi">&lt;/figure>&lt;p>图片中的ppi为：&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/gJDJoOZ.png" alt="Imgur">&lt;/figure>&lt;p>ppi和dpi是两个不同的概念，暂且理解成是一样的。&lt;/p>
&lt;p>而160是基准的像素密度。&lt;/p>
&lt;blockquote>
&lt;p>为什么是160看到说法没找到官方文档，如果你看到了烦请分享出来&lt;/p>
&lt;/blockquote>
&lt;p>就相当于，认为160的密度是标准的，比这个值大的，就表示一英寸这个单位上像素数量要多。屏幕尺寸相同的情况下，这个屏幕就有更高的分辨率，要容纳下这些像素，肯定是要要在一英寸上放下更多的像素了。&lt;/p>
&lt;p>&lt;code>dpi / 160&lt;/code>就表示要容纳多少倍的像素，再&lt;code>*dp&lt;/code>就表示一英寸上实际有多少px。&lt;/p>
&lt;p>用dp写布局就能解决在设备A和设备B上适配的一部分问题。在设备A的dpi是设备B的dpi 1.5倍时，两个设备换算出宽度的dp值是相同的，这时用dp就能用一套布局在两个不同设备上有相同的显示效果。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/YdBNH4S.png" alt="显示效果示例">&lt;/figure>&lt;p>你可能发现了规律，设备A是设备B的屏幕尺寸、宽、高都缩放1.5的结果。如果不都是成比例的呢？&lt;/p>
&lt;h2 class="heading" id="最小宽度sw">
 最小宽度（sw）&lt;span class="heading__anchor"> &lt;a href="#%e6%9c%80%e5%b0%8f%e5%ae%bd%e5%ba%a6sw">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>最小宽度方案是使用最多的方案。&lt;/p>
&lt;p>系统会根据当前设备屏幕的 最小宽度 来匹配 values-sw&lt;N>dp，用最小宽度是因为最小宽度不会随着旋转屏幕变化。&lt;/p>
&lt;p>以一个最小宽度作为基准，比如最小宽度基准是360dp，把屏幕分成360份。最小宽度是400dp时，把400分成360份，那么每一份是基准的1.11倍，sw400的dimen值就是sw360对应的1.11倍。在sw360中的1dp在sw400中要用1.11dp。&lt;/p>
&lt;p>关于最小宽度更详细参见&lt;a href="https://juejin.cn/post/6844903681524006925">骚年你的屏幕适配方式该升级了!-smallestWidth 限定符适配方案&lt;/a>&lt;/p>
&lt;h2 class="heading" id="补充">
 补充&lt;span class="heading__anchor"> &lt;a href="#%e8%a1%a5%e5%85%85">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="官方匹配规则">
 官方匹配规则&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%98%e6%96%b9%e5%8c%b9%e9%85%8d%e8%a7%84%e5%88%99">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>&lt;a href="https://developer.android.google.cn/guide/topics/resources/providing-resources?hl=zh-cn#AlternativeResources">官方匹配规则&lt;/a>是将多个配置限定符按照优先级先后拼接在一起，从前往后匹配，是可以向下兼容的，但是只要遇到一个不兼容的配置限定符就会回退。&lt;/p>
&lt;p>举个例子，现在项目中有两个layout文件夹，分别是layout-sw540-v31和layout-sw600-v33，设备的版本是32，分辨率是1920x1080，dpi是320，最小的宽度是1080px，计算出sw对应的dp是540。按照优先级sw比版本要高，优先匹配sw540，32和31、33都不同，但是刚刚说过是可以向下兼容的，因此可以命中layout-sw540-v31。&lt;/p>
&lt;h3 class="heading" id="sw计算">
 sw计算&lt;span class="heading__anchor"> &lt;a href="#sw%e8%ae%a1%e7%ae%97">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>官方文档：&lt;/p>
&lt;blockquote>
&lt;p>应用可用屏幕区域的最短尺寸。具体而言，应用窗口的 smallestWidth 是窗口可用高度和宽度的最小尺寸。您也可将其视为窗口的“最小可能宽度”。您可使用此限定符，让应用界面的可用宽度至少为 &lt;N> dp。&lt;/p>
&lt;/blockquote>
&lt;p>sw的单位是dp，二分辨率的单位是px，所以需要转换为dp。还用刚刚例子中的设备，最小的宽度是1080培px，假设dpi是320，计算dp的公式是&lt;code>px = dp * (dpi / 160)&lt;/code>，那么计算dp的公式就是&lt;code>dp=px / (dpi / 160)&lt;/code>，1080/(320/160)，sw的结果就是540。&lt;/p>
&lt;p>相关adb命令：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 设备分辨率&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">adb shell wm size
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看屏幕密度（DPI）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">adb shell wm density
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="插件仓库">
 插件仓库&lt;span class="heading__anchor"> &lt;a href="#%e6%8f%92%e4%bb%b6%e4%bb%93%e5%ba%93">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>&lt;a href="https://github.com/licheedev/SmallestWidthDimens">Github仓库&lt;/a>
按照上面最小宽度计算规则，帮我们为不同的屏幕密度生成对应的dimens文件夹，&lt;/p>
&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.android.com/training/multiscreen/screendensities?hl=zh-cn">支持不同的像素密度&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.cn/post/6844903681524006925">骚年你的屏幕适配方式该升级了!-SmallestWidth 限定符适配方案&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.cn/post/7000329806101348360">Android 最全面的屏幕适配方案&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.android.google.cn/guide/topics/resources/providing-resources?hl=zh-cn#AlternativeResources">官方匹配规则&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Material Design吸顶效果实现</title><link>https://opoojkk.com/posts/2024/11/05/material-sticky-effect/</link><pubDate>Tue, 05 Nov 2024 21:05:30 +0800</pubDate><guid>https://opoojkk.com/posts/2024/11/05/material-sticky-effect/</guid><description>&lt;h2 class="heading" id="前言">
 前言&lt;span class="heading__anchor"> &lt;a href="#%e5%89%8d%e8%a8%80">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Material Design是Google在2014年的I/O大会上发布的一种设计规范，包括布局、颜色、形状、声音、动效等。面向的是Android、iOS、Web和Flutter。对自己的Android，Google还推出了一系列组件、主题帮助开发者快速、方便的实现Material Design的效果。下面是几个能用在大部分实际项目中的控件：&lt;/p>
&lt;h2 class="heading" id="appbarlayout">
 AppBarLayout&lt;span class="heading__anchor"> &lt;a href="#appbarlayout">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>最近我们的项目首页UI改版，目前的布局大致如下图。打开首页时是显示tab的,向上滑动&lt;code>RecyclerView&lt;/code>时超过了一定距离(比如&lt;code>TabLayout&lt;/code>高度的50%)就隐藏&lt;code>TabLayout&lt;/code>，下滑时超过了这距离时再显示&lt;code>TabLayout&lt;/code>。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/xTIpxB1.png" alt="简图">&lt;/figure>&lt;p>搜索时，发现有人用CoordinatorLayout+AppBarLayout实现的把ToolBar隐藏的效果。于是把我们项目中的TabLayout用AppBarLayout给包起来。达到了与期望的效果差不多的。&lt;/p>
&lt;p>下面是简化了的布局。为了写起来方便用了RecyclerView，实际项目中用的则是Viewpager，除了页面左右滑动外其余都是相同的的。&lt;/p>
&lt;p>布局代码如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="err">?&lt;/span>&lt;span class="n">xml&lt;/span> &lt;span class="n">version&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;1.0&amp;#34;&lt;/span> &lt;span class="n">encoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="err">?&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">coordinatorlayout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">widget&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">CoordinatorLayout&lt;/span> &lt;span class="n">xmlns&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;http://schemas.android.com/apk/res/android&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">xmlns&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;http://schemas.android.com/apk/res-auto&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">xmlns&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">tools&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;http://schemas.android.com/tools&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tools&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;.appbar.activity.OtherActivity&amp;#34;&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">google&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">material&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">appbar&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">AppBarLayout&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;wrap_content&amp;#34;&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">google&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">material&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tabs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TabLayout&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">background&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;@color/teal_200&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_scrollFlags&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;scroll|enterAlways|snap|snapMargins&amp;#34;&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">google&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">material&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tabs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TabItem&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;wrap_content&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;wrap_content&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Monday&amp;#34;&lt;/span>&lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">google&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">material&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tabs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TabItem&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;wrap_content&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;wrap_content&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Tuesday&amp;#34;&lt;/span>&lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">google&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">material&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tabs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TabItem&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;wrap_content&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;wrap_content&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Wednesday&amp;#34;&lt;/span>&lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;/&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">google&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">material&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tabs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TabLayout&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;/&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">google&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">material&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">appbar&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">AppBarLayout&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">recyclerview&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">widget&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">RecyclerView&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;@+id/content_rv&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">android&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">layout_behavior&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;@string/appbar_scrolling_view_behavior&amp;#34;&lt;/span>&lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;/&lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">coordinatorlayout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">widget&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">CoordinatorLayout&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>布局文件中最外层需要使用&lt;code>CoordinatorLayout&lt;/code>，其他的就是&lt;code>TabLayout&lt;/code>和一个&lt;code>RecyclerView&lt;/code>。其中重要的是&lt;code>layout_scrollFlags&lt;/code>和&lt;code>layout_behavior&lt;/code>。&lt;/p>
&lt;p>&lt;code>layout_behavior&lt;/code>的作用是在有&lt;code>layout_behavior&lt;/code>的&lt;code>CoordinatorLayout&lt;/code>子View(&lt;em>​&lt;code>NestedScrollView&lt;/code>​&lt;/em>​ &lt;em>、&lt;em>​&lt;/em>​&lt;code>RecyclerView&lt;/code>​&lt;/em>​&lt;em>等&lt;/em>)滚动时可以根据&lt;code>layout_scrollFlags&lt;/code>改变AppBarLayout的行为。&lt;/p>
&lt;p>&lt;code>layout_behavior&lt;/code>是官方定义的，用&lt;code>appbar_scrolling_view_behavior&lt;/code>。&lt;code>layout_scrollFlags&lt;/code>可以根据我们需要的效果选择。&lt;/p>
&lt;h2 class="heading" id="layout_scrollflags">
 layout_scrollFlags&lt;span class="heading__anchor"> &lt;a href="#layout_scrollflags">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在官方文档中，&lt;code>layout_scrollFlags&lt;/code>的取值可以有以下七种：&lt;/p>
&lt;h3 class="heading" id="noscroll">
 noScroll&lt;span class="heading__anchor"> &lt;a href="#noscroll">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>不受&lt;code>RecyclerView&lt;/code>或*&lt;code>NestedScrollView&lt;/code>的滑动影响。*&lt;/p>
&lt;h3 class="heading" id="scroll">
 scroll&lt;span class="heading__anchor"> &lt;a href="#scroll">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>AppBarLayout在&lt;code>RecyclerView&lt;/code>的第一个item或*&lt;code>NestedScrollView&lt;/code>上方随着其滑动。类似插入到*&lt;code>RecyclerView&lt;/code>第0个位置上。&lt;/p>
&lt;h3 class="heading" id="exituntilcollapsed">
 exitUntilCollapsed&lt;span class="heading__anchor"> &lt;a href="#exituntilcollapsed">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>需要与&lt;code>scroll&lt;/code>一起使用，且可以设置&lt;code>minHeight&lt;/code>。当向上滑动时顶部会有&lt;code>minHeight&lt;/code>高度的&lt;code>TabLayout&lt;/code>一直在顶部；向下滑动不会有变化，直到第0个位置的item完全显示。这时再向下滑动会逐渐恢复&lt;code>TabLayout&lt;/code>原来的高度。&lt;/p>
&lt;p>&lt;code>minHeight&lt;/code>可以设置0dp，这时与只设置&lt;code>scroll&lt;/code>无异。&lt;/p>
&lt;h3 class="heading" id="enteralways">
 enterAlways&lt;span class="heading__anchor"> &lt;a href="#enteralways">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>需要与&lt;code>scroll&lt;/code>一起使用。在任何位置，向下滑动会隐藏&lt;code>TabLayout&lt;/code>，向上滑动会显示&lt;code>TabLayout&lt;/code>。且可以停留在TabLayout任意高度。&lt;/p>
&lt;h3 class="heading" id="enteralwayscollapsed">
 enterAlwaysCollapsed&lt;span class="heading__anchor"> &lt;a href="#enteralwayscollapsed">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>需要与&lt;code>scroll&lt;/code>、&lt;code>enterAlways&lt;/code>一起使用，且可以设置&lt;code>minHeight&lt;/code>。向下滑动时与&lt;code>enterAlways&lt;/code>相同。当向下滑动时会有&lt;code>minHeight&lt;/code>高度的&lt;code>TabLayout&lt;/code>一直在顶部，直到第0个位置的item完全显示。这时再向下滑动会逐渐恢复&lt;code>TabLayout&lt;/code>原来的高度。&lt;/p>
&lt;h3 class="heading" id="snap">
 snap&lt;span class="heading__anchor"> &lt;a href="#snap">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>需要与&lt;code>scroll&lt;/code>一起使用。与只是用&lt;code>scroll&lt;/code>不同的只在&lt;code>TabLayout&lt;/code>的显示和隐藏上，无论显示还隐藏只有&lt;code>TabLayout&lt;/code>高度的一半时，才会变化，否则会回到当前的状态。&lt;/p>
&lt;h3 class="heading" id="snapmargins">
 snapMargins&lt;span class="heading__anchor"> &lt;a href="#snapmargins">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>需要与&lt;code>scroll&lt;/code>、&lt;code>snap&lt;/code>一起使用。当使用&lt;code>snap&lt;/code>时，在TabLayout中设置&lt;code>marginBottom&lt;/code>，设置的marginBottom不会随着TabLayout一起，会有&lt;code>marginBottom&lt;/code>高度的一块留在顶部，需要再次滑动才能收起来。这就是&lt;code>snapMargins&lt;/code>的使用场景，设置了&lt;code>marginBottom&lt;/code>会随着TabLayout一起收起。&lt;/p>
&lt;p>上面几种的效果其实都不是我们想要的。&lt;code>enterAlways&lt;/code>没有吸顶的效果，意味着TabLayout可以显示一部分，这时用户点击Tab到其他内容页时，TabLayout的显示就会异常。而使用&lt;code>snap&lt;/code>时，当&lt;code>TabLayout&lt;/code>已经隐藏了，如果用户滑动到一半了就需要先回到第0个item的位置，再滑动才能显示&lt;code>TabLayout&lt;/code>，体验很差。&lt;/p>
&lt;p>既然有&lt;code>snap&lt;/code>有吸顶效果，&lt;code>enterAlways&lt;/code>又可以在任何位置位置滑出/隐藏TabLayout。有没有可能实现结合的效果呢？把两个flag都写在&lt;code>TabLayout&lt;/code>上，确实实现了。但是没有文档说可以这么用，很迷惑。去看了下AppBarLayout的源码，&lt;code>CoordinatorLayout&lt;/code>和&lt;code>AppBarLayout&lt;/code>联动原理不再赘述。&lt;/p>
&lt;p>&lt;code>snap&lt;/code>的效果与&lt;code>enterAlways&lt;/code>实际上是分开的。设置了&lt;code>SCROLL_FLAG_ENTER_ALWAYS&lt;/code>后，从任意位置都能滑出TabLayout。又因为有snap的效果，手指松开结束滑动之后又会有吸顶的效果。&lt;/p></description></item><item><title>Material Design滚动变色效果实现</title><link>https://opoojkk.com/posts/2024/11/04/implement-material-style-action-bar-color-change-on-scroll/</link><pubDate>Mon, 04 Nov 2024 07:25:50 +0800</pubDate><guid>https://opoojkk.com/posts/2024/11/04/implement-material-style-action-bar-color-change-on-scroll/</guid><description>&lt;p>许许多多Material风格的Android应用支持滚动后改变Action Bar和状态栏的颜色, 放一张示例图:&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/rMa00Ek.gif" alt="LibChecker">&lt;/figure>&lt;h2 class="heading" id="实现">
 实现&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%9e%e7%8e%b0">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="外层viewgroup改用coordinatorlayout">
 外层ViewGroup改用CoordinatorLayout&lt;span class="heading__anchor"> &lt;a href="#%e5%a4%96%e5%b1%82viewgroup%e6%94%b9%e7%94%a8coordinatorlayout">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>CoordinatorLayout才支持内部View滚动后调用behavior的方法, 自定义View也能实现.&lt;/p>
&lt;h3 class="heading" id="滚动view添加flag">
 滚动View添加flag&lt;span class="heading__anchor"> &lt;a href="#%e6%bb%9a%e5%8a%a8view%e6%b7%bb%e5%8a%a0flag">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">app:layout_behavior=&amp;#34;@string/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">appbar_scrolling_view_behavior&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="做出反应的view添加style">
 做出反应的View添加style&lt;span class="heading__anchor"> &lt;a href="#%e5%81%9a%e5%87%ba%e5%8f%8d%e5%ba%94%e7%9a%84view%e6%b7%bb%e5%8a%a0style">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">android:theme=&amp;#34;@style/ThemeOverlay.AppCompat.Dark.ActionBar&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>效果图:&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/HrM5R86.gif" alt="实现效果">&lt;/figure>&lt;h3 class="heading" id="完成代码">
 完成代码&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%8c%e6%88%90%e4%bb%a3%e7%a0%81">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;androidx.coordinatorlayout.widget.CoordinatorLayout&lt;/span> &lt;span class="na">xmlns:android=&lt;/span>&lt;span class="s">&amp;#34;http://schemas.android.com/apk/res/android&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xmlns:app=&lt;/span>&lt;span class="s">&amp;#34;http://schemas.android.com/apk/res-auto&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xmlns:tools=&lt;/span>&lt;span class="s">&amp;#34;http://schemas.android.com/tools&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:id=&lt;/span>&lt;span class="s">&amp;#34;@+id/main&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:layout_width=&lt;/span>&lt;span class="s">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:layout_height=&lt;/span>&lt;span class="s">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">tools:context=&lt;/span>&lt;span class="s">&amp;#34;.home.MainActivity&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- AppBarLayout，include Toolbar --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;com.google.android.material.appbar.AppBarLayout&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:id=&lt;/span>&lt;span class="s">&amp;#34;@+id/appBarLayout&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:layout_width=&lt;/span>&lt;span class="s">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:layout_height=&lt;/span>&lt;span class="s">&amp;#34;wrap_content&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:layout_gravity=&lt;/span>&lt;span class="s">&amp;#34;top&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:fitsSystemWindows=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:theme=&lt;/span>&lt;span class="s">&amp;#34;@style/ThemeOverlay.AppCompat.Dark.ActionBar&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">app:liftOnScroll=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;androidx.appcompat.widget.Toolbar&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:id=&lt;/span>&lt;span class="s">&amp;#34;@+id/toolbar&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">style=&lt;/span>&lt;span class="s">&amp;#34;?actionBarStyle&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:layout_width=&lt;/span>&lt;span class="s">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:layout_height=&lt;/span>&lt;span class="s">&amp;#34;wrap_content&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:background=&lt;/span>&lt;span class="s">&amp;#34;@null&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">app:titleTextColor=&lt;/span>&lt;span class="s">&amp;#34;@color/black&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/com.google.android.material.appbar.AppBarLayout&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;androidx.recyclerview.widget.RecyclerView&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:id=&lt;/span>&lt;span class="s">&amp;#34;@+id/rv_installed_app&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:layout_width=&lt;/span>&lt;span class="s">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:layout_height=&lt;/span>&lt;span class="s">&amp;#34;match_parent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">android:scrollbars=&lt;/span>&lt;span class="s">&amp;#34;vertical&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">app:layout_behavior=&lt;/span>&lt;span class="s">&amp;#34;@string/appbar_scrolling_view_behavior&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/androidx.coordinatorlayout.widget.CoordinatorLayout&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>注: 不要忘记设置SupportActionBar&lt;/p></description></item><item><title>验证suspend all threads</title><link>https://opoojkk.com/posts/2024/11/02/verify-suspension-of-all-threads/</link><pubDate>Sat, 02 Nov 2024 09:05:36 +0800</pubDate><guid>https://opoojkk.com/posts/2024/11/02/verify-suspension-of-all-threads/</guid><description>&lt;h1 class="heading" id="验证suspend-all-threads">
 验证suspend all threads&lt;span class="heading__anchor"> &lt;a href="#%e9%aa%8c%e8%af%81suspend-all-threads">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>对应KOOM实现流程, 结合对比几种场景, 验证KOOM中fork前suspend和resume后作用.&lt;/p>
&lt;h2 class="heading" id="suspend和resume">
 suspend和resume&lt;span class="heading__anchor"> &lt;a href="#suspend%e5%92%8cresume">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>suspendAndFork&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">pid_t&lt;/span> &lt;span class="n">HprofDump&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">SuspendAndFork&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">KCHECKI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">init_done_&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">android_api_&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">__ANDROID_API_R__&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">suspend_vm_fnc_&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="nf">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">android_api_&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">__ANDROID_API_S__&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">self&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">__get_tls&lt;/span>&lt;span class="p">()[&lt;/span>&lt;span class="n">TLS_SLOT_ART_THREAD_SELF&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sgc_constructor_fnc_&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">sgc_instance_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">kGcCauseHprof&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">kCollectorTypeHprof&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ssa_constructor_fnc_&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">ssa_instance_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">LOG_TAG&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// avoid deadlock with child process
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">exclusive_unlock_fnc_&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">mutator_lock_ptr_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">self&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sgc_destructor_fnc_&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">sgc_instance_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pid_t&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">fork&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">pid&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Set timeout for child process
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">alarm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">60&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prctl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PR_SET_NAME&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;forked-dump-process&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">pid&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>resumeAndWait&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="n">HprofDump&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ResumeAndWait&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pid_t&lt;/span> &lt;span class="n">pid&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">KCHECKB&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">init_done_&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">android_api_&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">__ANDROID_API_R__&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">resume_vm_fnc_&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="nf">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">android_api_&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">__ANDROID_API_S__&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">self&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">__get_tls&lt;/span>&lt;span class="p">()[&lt;/span>&lt;span class="n">TLS_SLOT_ART_THREAD_SELF&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exclusive_lock_fnc_&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">mutator_lock_ptr_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">self&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ssa_destructor_fnc_&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">ssa_instance_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">status&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(;;)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">waitpid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">status&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">WIFEXITED&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ALOGE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Child process %d exited with status %d, terminated by signal %d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">WEXITSTATUS&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">WTERMSIG&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// if waitpid is interrupted by the signal,just call it again
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">errno&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">EINTR&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="suspend验证">
 suspend验证&lt;span class="heading__anchor"> &lt;a href="#suspend%e9%aa%8c%e8%af%81">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>运行环境：一部虚拟机Android 9，一部物理机器Android 13&lt;/p>
&lt;p>除Android13标题下的, 环境均为Android9的虚拟机.&lt;/p>
&lt;h3 class="heading" id="运行koom-demo">
 运行KOOM Demo&lt;span class="heading__anchor"> &lt;a href="#%e8%bf%90%e8%a1%8ckoom-demo">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.346&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12889&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nf">startLoop&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.412&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12940&lt;/span> &lt;span class="nx">OOMMonitor_SystemInfo&lt;/span>   &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="o">----&lt;/span>&lt;span class="nx">OOM&lt;/span> &lt;span class="nx">Monitor&lt;/span> &lt;span class="nx">Memory&lt;/span>&lt;span class="o">----&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.412&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12940&lt;/span> &lt;span class="nx">OOMMonitor_SystemInfo&lt;/span>   &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="p">[&lt;/span>&lt;span class="nx">java&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="nx">max&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">536870912&lt;/span> &lt;span class="nx">used&lt;/span> &lt;span class="nx">ratio&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="o">%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.412&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12940&lt;/span> &lt;span class="nx">OOMMonitor_SystemInfo&lt;/span>   &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="p">[&lt;/span>&lt;span class="nx">proc&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="nx">VmSize&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2695044&lt;/span>&lt;span class="nx">kB&lt;/span> &lt;span class="nx">VmRss&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">135440&lt;/span>&lt;span class="nx">kB&lt;/span> &lt;span class="nx">Threads&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">715&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.412&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12940&lt;/span> &lt;span class="nx">OOMMonitor_SystemInfo&lt;/span>   &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="p">[&lt;/span>&lt;span class="nx">meminfo&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="nx">MemTotal&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2042232&lt;/span>&lt;span class="nx">kB&lt;/span> &lt;span class="nx">MemFree&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">352144&lt;/span>&lt;span class="nx">kB&lt;/span> &lt;span class="nx">MemAvailable&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">881440&lt;/span>&lt;span class="nx">kB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.412&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12940&lt;/span> &lt;span class="nx">OOMMonitor_SystemInfo&lt;/span>   &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">avaliable&lt;/span> &lt;span class="nx">ratio&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">43&lt;/span>&lt;span class="o">%&lt;/span> &lt;span class="nx">CmaTotal&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">303104&lt;/span>&lt;span class="nx">kB&lt;/span> &lt;span class="nx">ION_heap&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="nx">kB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.412&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12940&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">OOMTracker&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="p">[&lt;/span>&lt;span class="nx">meet&lt;/span> &lt;span class="nx">condition&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="nx">overThresholdCount&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">threadCount&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">715&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.413&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12940&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">OOMTracker&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">over&lt;/span> &lt;span class="nx">threshold&lt;/span> &lt;span class="nx">dumpThreadIfNeed&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.833&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12940&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">OOMTracker&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">threadNames&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Jit&lt;/span> &lt;span class="nx">thread&lt;/span> &lt;span class="nx">pool&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Signal&lt;/span> &lt;span class="nx">Catcher&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">ADB&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">JDWP&lt;/span> &lt;span class="nx">Connec&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">HeapTaskDaemon&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">ReferenceQueueD&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">FinalizerDaemon&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">FinalizerWatchd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Binder&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">12889_1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Binder&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">12889_2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Binder&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">12889_3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Profile&lt;/span> &lt;span class="nx">Saver&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">RenderThread&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">queued&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">work&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">loo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">LoopThread&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">6&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">9&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">14&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">15&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">18&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">23&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">25&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">28&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">30&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">31&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">33&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">35&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">36&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">37&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">38&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">39&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">41&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">42&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">43&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">44&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">45&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">46&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">47&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">48&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">49&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">51&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">52&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">53&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">54&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">55&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">56&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">57&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">58&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">59&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">60&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">61&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">62&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">63&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">64&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">65&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">66&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">67&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">68&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">69&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">70&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">71&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">72&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">73&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">74&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">75&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">76&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">77&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">78&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">79&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">80&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">81&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">82&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">83&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">84&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">85&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">86&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">87&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">88&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">89&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">90&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">91&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">92&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">93&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">94&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">95&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">96&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">97&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">98&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">99&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">101&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">102&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">103&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">104&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">105&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">106&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">107&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">108&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">109&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">110&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">111&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">112&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">113&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">114&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">115&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">116&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">117&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">118&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">119&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">120&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">121&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">122&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">123&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">124&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">125&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">126&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">127&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">129&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">130&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">131&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">132&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">133&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">134&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">135&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">136&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">137&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">138&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">139&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">140&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">141&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">142&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">143&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">144&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">145&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">146&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">147&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">148&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">149&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">150&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">151&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">152&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">153&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">154&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">155&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">156&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">157&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">158&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">159&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">160&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">161&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">162&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">163&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">164&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">165&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">166&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">167&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">168&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">169&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">170&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">171&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">172&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">173&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">174&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">175&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">176&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">177&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">178&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">179&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">180&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">181&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">182&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">183&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">184&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">185&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">186&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">187&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">188&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">189&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">190&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">191&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">192&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">193&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">194&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">195&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">196&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">197&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">198&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">199&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">200&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">201&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">202&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">203&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">204&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">205&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">206&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">207&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">208&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">209&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">210&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">211&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">212&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">213&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">214&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">215&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">216&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">217&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">218&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">219&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">220&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">221&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">222&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">223&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">224&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">225&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">226&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">227&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">228&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">229&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">230&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">231&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">232&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">233&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">234&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">235&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">236&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">237&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">238&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">239&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">240&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">241&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">242&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">243&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">244&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">245&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">246&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">247&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">248&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">249&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">250&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">251&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">252&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">253&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">254&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">255&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">256&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">257&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">258&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">259&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">260&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">261&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">262&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">263&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">264&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">265&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">266&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">267&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">268&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">269&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">270&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">271&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">272&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">273&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">274&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">275&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">276&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">277&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">278&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">279&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">280&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">281&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">282&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">283&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">284&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">285&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">286&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">287&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">288&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">289&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">290&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">291&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">292&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">293&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">294&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">295&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">296&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">297&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">298&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">299&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">300&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">301&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">302&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">303&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">304&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">305&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">306&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">307&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">308&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">309&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">310&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">311&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">312&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">313&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">314&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">315&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">316&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">317&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">318&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">319&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">320&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">321&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">322&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">323&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">324&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">325&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">T&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.847&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12940&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstAnalysisTime&lt;/span>&lt;span class="p">():&lt;/span>&lt;span class="mi">1709003366019&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.847&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12940&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getAnalysisTimes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.849&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13644&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">mTrackReasons&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="nx">reason_thread_oom&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.850&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13644&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dumpAndAnalysis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.852&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13645&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">processHprofFile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.852&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13645&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">thread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.863&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13645&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">old&lt;/span> &lt;span class="nx">files&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.871&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13644&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">analysis&lt;/span> &lt;span class="nx">dir&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.872&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13644&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mf">1.0.0&lt;/span>&lt;span class="nx">_2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27_11&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">30_851&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hprof&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.894&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13644&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">before&lt;/span> &lt;span class="nx">suspend&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">fork&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.954&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13645&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">fd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">STARTED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">13646&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">32.199&lt;/span> &lt;span class="mi">13646&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13646&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">spend&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1305&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">32.206&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13644&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">wait&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1312&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">32.206&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13644&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">notify&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="nx">pid&lt;/span> &lt;span class="mi">13646&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">32.206&lt;/span> &lt;span class="mi">12889&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13644&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">end&lt;/span> &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">dump&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">ENDED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">13646&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;!-- 结论: fork后，子进程只保留执行fork的一个线程 -->
&lt;h3 class="heading" id="多线程环境">
 多线程环境&lt;span class="heading__anchor"> &lt;a href="#%e5%a4%9a%e7%ba%bf%e7%a8%8b%e7%8e%af%e5%a2%83">#&lt;/a>&lt;/span>
&lt;/h3>&lt;h4 class="heading" id="主线程fork">
 主线程fork&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%bb%e7%ba%bf%e7%a8%8bfork">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>Java创建线程调用C++ pthread创建&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Java创建线程
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">Thread_nativeCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">JNIEnv&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">jclass&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">jobject&lt;/span> &lt;span class="n">java_thread&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">jlong&lt;/span> &lt;span class="n">stack_size&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">jboolean&lt;/span> &lt;span class="n">daemon&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Thread&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateNativeThread&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">java_thread&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">stack_size&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">daemon&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">JNI_TRUE&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="n">Thread&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateNativeThread&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">JNIEnv&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">jobject&lt;/span> &lt;span class="n">java_peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">stack_size&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">is_daemon&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CHECK&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">java_peer&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pthread_t&lt;/span> &lt;span class="n">new_pthread&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pthread_attr_t&lt;/span> &lt;span class="n">attr&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CHECK_PTHREAD_CALL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pthread_attr_init&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">attr&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="s">&amp;#34;new thread&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CHECK_PTHREAD_CALL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pthread_attr_setdetachstate&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">attr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">PTHREAD_CREATE_DETACHED&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="s">&amp;#34;PTHREAD_CREATE_DETACHED&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CHECK_PTHREAD_CALL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pthread_attr_setstacksize&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">attr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">stack_size&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">stack_size&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">pthread_create_result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pthread_create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">new_pthread&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">attr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Thread&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateCallback&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">child_thread&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CHECK_PTHREAD_CALL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pthread_attr_destroy&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">attr&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="s">&amp;#34;new thread&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 class="heading" id="子线程fork">
 子线程fork&lt;span class="heading__anchor"> &lt;a href="#%e5%ad%90%e7%ba%bf%e7%a8%8bfork">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;pthread.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;sys/types.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;unistd.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;chrono&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;thread&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nf">func1&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">this_thread&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">sleep_for&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">milliseconds&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1000&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;func1 is printing.&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pthread_t&lt;/span> &lt;span class="n">tid&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pthread_create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">tid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">func1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pid_t&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">fork&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 子进程
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">pid&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="o">--&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;child process&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>打印信息：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">func1&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">printing&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">func1&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">printing&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">func1&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">printing&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">func1&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">printing&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">child&lt;/span> &lt;span class="n">process&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">child&lt;/span> &lt;span class="n">process&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">child&lt;/span> &lt;span class="n">process&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">child&lt;/span> &lt;span class="n">process&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">child&lt;/span> &lt;span class="n">process&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>子线程执行&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;pthread.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;sys/types.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;unistd.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;chrono&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;thread&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nf">func1&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// fork()出一个子进程
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">pid_t&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">fork&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">pid&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 创建子进程失败
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Failed to fork a child process&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">pid&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 子进程
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Child process pid = %d&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">getpid&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;func1: my pid = %d&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">getpid&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 父进程
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Parent process pid = %d, child pid = %d&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">getpid&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">pid&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pthread_t&lt;/span> &lt;span class="n">tid&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pthread_create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">tid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">func1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 主线程等待一段时间
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;main : my pid = %d&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">getpid&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>打印日志&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Child&lt;/span> &lt;span class="n">process&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">56330&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nl">func1&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">my&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">56330&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nl">func1&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">my&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">56330&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nl">func1&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">my&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">56330&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nl">func1&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">my&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">56330&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nl">func1&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">my&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">56330&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nl">func1&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">my&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">56330&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nl">func1&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">my&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">56330&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nl">func1&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">my&lt;/span> &lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">56330&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="不执行suspend和resume">
 不执行suspend和resume&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%8d%e6%89%a7%e8%a1%8csuspend%e5%92%8cresume">#&lt;/a>&lt;/span>
&lt;/h3>&lt;h4 class="heading" id="没有子线程运行running">
 没有子线程运行（Running）&lt;span class="heading__anchor"> &lt;a href="#%e6%b2%a1%e6%9c%89%e5%ad%90%e7%ba%bf%e7%a8%8b%e8%bf%90%e8%a1%8crunning">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>注释C++调用suspend和resume方法&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">42.395&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">10991&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nf">startLoop&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.452&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11040&lt;/span> &lt;span class="nx">OOMMonitor_SystemInfo&lt;/span>   &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="o">----&lt;/span>&lt;span class="nx">OOM&lt;/span> &lt;span class="nx">Monitor&lt;/span> &lt;span class="nx">Memory&lt;/span>&lt;span class="o">----&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.452&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11040&lt;/span> &lt;span class="nx">OOMMonitor_SystemInfo&lt;/span>   &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="p">[&lt;/span>&lt;span class="nx">java&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="nx">max&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">536870912&lt;/span> &lt;span class="nx">used&lt;/span> &lt;span class="nx">ratio&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="o">%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.452&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11040&lt;/span> &lt;span class="nx">OOMMonitor_SystemInfo&lt;/span>   &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="p">[&lt;/span>&lt;span class="nx">proc&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="nx">VmSize&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2685572&lt;/span>&lt;span class="nx">kB&lt;/span> &lt;span class="nx">VmRss&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">138468&lt;/span>&lt;span class="nx">kB&lt;/span> &lt;span class="nx">Threads&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">715&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.452&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11040&lt;/span> &lt;span class="nx">OOMMonitor_SystemInfo&lt;/span>   &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="p">[&lt;/span>&lt;span class="nx">meminfo&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="nx">MemTotal&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2042232&lt;/span>&lt;span class="nx">kB&lt;/span> &lt;span class="nx">MemFree&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">368948&lt;/span>&lt;span class="nx">kB&lt;/span> &lt;span class="nx">MemAvailable&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">893380&lt;/span>&lt;span class="nx">kB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.453&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11040&lt;/span> &lt;span class="nx">OOMMonitor_SystemInfo&lt;/span>   &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">avaliable&lt;/span> &lt;span class="nx">ratio&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">43&lt;/span>&lt;span class="o">%&lt;/span> &lt;span class="nx">CmaTotal&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">303104&lt;/span>&lt;span class="nx">kB&lt;/span> &lt;span class="nx">ION_heap&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="nx">kB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.453&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11040&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">OOMTracker&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="p">[&lt;/span>&lt;span class="nx">meet&lt;/span> &lt;span class="nx">condition&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="nx">overThresholdCount&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">threadCount&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">715&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.453&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11040&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">OOMTracker&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">over&lt;/span> &lt;span class="nx">threshold&lt;/span> &lt;span class="nx">dumpThreadIfNeed&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.731&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11040&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">OOMTracker&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">threadNames&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Jit&lt;/span> &lt;span class="nx">thread&lt;/span> &lt;span class="nx">pool&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Signal&lt;/span> &lt;span class="nx">Catcher&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">ADB&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">JDWP&lt;/span> &lt;span class="nx">Connec&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">HeapTaskDaemon&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">ReferenceQueueD&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">FinalizerDaemon&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">FinalizerWatchd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Binder&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">10991_1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Binder&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">10991_2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Binder&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">10991_3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Profile&lt;/span> &lt;span class="nx">Saver&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">RenderThread&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">queued&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">work&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">loo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">LoopThread&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">6&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">9&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">14&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">15&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">17&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">18&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">23&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">25&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">28&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">30&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">31&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">33&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">35&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">36&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">37&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">38&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">39&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">41&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">42&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">43&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">44&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">45&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">46&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">47&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">48&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">49&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">51&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">52&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">53&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">54&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">55&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">56&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">57&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">58&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">59&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">60&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">61&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">62&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">63&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">64&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">65&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">66&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">67&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">68&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">69&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">70&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">71&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">72&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">73&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">74&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">75&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">76&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">77&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">78&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">79&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">80&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">81&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">82&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">83&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">84&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">85&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">86&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">87&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">88&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">89&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">90&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">91&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">92&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">93&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">94&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">95&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">96&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">97&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">98&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">99&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">101&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">102&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">103&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">104&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">105&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">106&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">107&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">108&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">109&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">110&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">111&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">112&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">113&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">114&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">115&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">116&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">117&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">118&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">119&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">120&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">121&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">122&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">123&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">124&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">125&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">126&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">127&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">129&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">130&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">131&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">132&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">133&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">134&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">135&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">136&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">137&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">138&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">139&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">140&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">141&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">142&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">143&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">144&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">145&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">146&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">147&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">148&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">149&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">150&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">151&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">152&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">153&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">154&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">155&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">156&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">157&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">158&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">159&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">160&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">161&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">162&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">163&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">164&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">165&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">166&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">167&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">168&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">169&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">170&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">171&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">172&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">173&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">174&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">175&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">176&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">177&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">178&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">179&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">180&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">181&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">182&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">183&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">184&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">185&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">186&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">187&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">188&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">189&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">190&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">191&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">192&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">193&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">194&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">195&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">196&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">197&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">198&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">199&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">200&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">201&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">202&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">203&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">204&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">205&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">206&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">207&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">208&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">209&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">210&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">211&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">212&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">213&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">214&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">215&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">216&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">217&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">218&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">219&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">220&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">221&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">222&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">223&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">224&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">225&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">226&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">227&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">228&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">229&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">230&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">231&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">232&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">233&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">234&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">235&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">236&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">237&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">238&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">239&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">240&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">241&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">242&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">243&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">244&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">245&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">246&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">247&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">248&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">249&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">250&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">251&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">252&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">253&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">254&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">255&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">256&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">257&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">258&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">259&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">260&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">261&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">262&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">263&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">264&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">265&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">266&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">267&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">268&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">269&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">270&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">271&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">272&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">273&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">274&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">275&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">276&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">277&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">278&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">279&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">280&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">281&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">282&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">283&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">284&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">285&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">286&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">287&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">288&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">289&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">290&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">291&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">292&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">293&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">294&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">295&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">296&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">297&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">298&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">299&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">300&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">301&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">302&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">303&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">304&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">305&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">306&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">307&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">308&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">309&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">310&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">311&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">312&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">313&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">314&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">315&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">316&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">317&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">318&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">319&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">320&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">321&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">322&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">323&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">324&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Thread&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">325&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">T&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.738&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11040&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstAnalysisTime&lt;/span>&lt;span class="p">():&lt;/span>&lt;span class="mi">1709003366019&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.738&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11040&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getAnalysisTimes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.741&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11746&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">mTrackReasons&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="nx">reason_thread_oom&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.741&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11746&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dumpAndAnalysis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.743&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11747&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">processHprofFile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.743&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11747&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">thread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.752&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11747&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">old&lt;/span> &lt;span class="nx">files&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.753&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11746&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">analysis&lt;/span> &lt;span class="nx">dir&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.754&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11746&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mf">1.0.0&lt;/span>&lt;span class="nx">_2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27_11&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">47_742&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hprof&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.762&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11747&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">fd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.771&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11746&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">before&lt;/span> &lt;span class="nx">suspend&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">fork&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">STARTED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">11748&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">48.920&lt;/span> &lt;span class="mi">11748&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11748&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">spend&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1149&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">48.927&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11746&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">wait&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1156&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">48.927&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11746&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">notify&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="nx">pid&lt;/span> &lt;span class="mi">11748&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">27&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">48.927&lt;/span> &lt;span class="mi">10991&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">11746&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">end&lt;/span> &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">dump&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">ENDED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">11748&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 class="heading" id="父进程fork前有子线程在运行子进程dump">
 父进程fork前有子线程在运行，子进程dump&lt;span class="heading__anchor"> &lt;a href="#%e7%88%b6%e8%bf%9b%e7%a8%8bfork%e5%89%8d%e6%9c%89%e5%ad%90%e7%ba%bf%e7%a8%8b%e5%9c%a8%e8%bf%90%e8%a1%8c%e5%ad%90%e8%bf%9b%e7%a8%8bdump">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>fork前启动了一个线程，死循环执行&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.053&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">2657&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstAnalysisTime&lt;/span>&lt;span class="p">():&lt;/span>&lt;span class="mi">1709191024825&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.053&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">2657&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getAnalysisTimes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.056&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">mTrackReasons&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="nx">reason_thread_oom&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.056&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dumpAndAnalysis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.061&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3571&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">processHprofFile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.061&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3571&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">thread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.071&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3571&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">create&lt;/span> &lt;span class="nx">json&lt;/span> &lt;span class="nx">file&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">then&lt;/span> &lt;span class="nx">start&lt;/span> &lt;span class="nx">service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.073&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3571&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">file&lt;/span> &lt;span class="nx">size&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.079&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">analysis&lt;/span> &lt;span class="nx">dir&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.080&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mf">1.0.0&lt;/span>&lt;span class="nx">_2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29_16&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="nx">_060&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hprof&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.090&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">active&lt;/span> &lt;span class="nx">thread&lt;/span> &lt;span class="nx">count&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">712&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.090&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">before&lt;/span> &lt;span class="nx">suspend&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">fork&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.126&lt;/span>  &lt;span class="mi">3577&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3577&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">pid&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3577&lt;/span>                             &lt;span class="nx">I&lt;/span>  &lt;span class="nx">child&lt;/span> &lt;span class="nx">process&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">STARTED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">3577&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">41&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.162&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">t1&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nx">RUNNABLE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">41&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.162&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">t2&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nx">TERMINATED&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">41&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.162&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">t3&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nx">TERMINATED&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">41&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.162&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">notify&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="nx">pid&lt;/span> &lt;span class="mi">3577&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">41&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.163&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">wait&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">60029&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">41&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">02.163&lt;/span>  &lt;span class="mi">2594&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3570&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">end&lt;/span> &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">dump&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>结果: 多了一个正在运行的线程&lt;/p>
&lt;p>&lt;img src="https://i.imgur.com/uXhQHEp.png" alt="image">子进程执行dump到60s结束&lt;/p>
&lt;h4 class="heading" id="父进程fork前有子线程在运行子进程suspend">
 父进程fork前有子线程在运行，子进程suspend&lt;span class="heading__anchor"> &lt;a href="#%e7%88%b6%e8%bf%9b%e7%a8%8bfork%e5%89%8d%e6%9c%89%e5%ad%90%e7%ba%bf%e7%a8%8b%e5%9c%a8%e8%bf%90%e8%a1%8c%e5%ad%90%e8%bf%9b%e7%a8%8bsuspend">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">41.932&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">18915&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nf">startLoop&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.540&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19076&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstAnalysisTime&lt;/span>&lt;span class="p">():&lt;/span>&lt;span class="mi">1709191024825&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.541&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19076&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getAnalysisTimes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.548&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19780&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">mTrackReasons&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="nx">reason_thread_oom&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.548&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19780&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dumpAndAnalysis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.551&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19781&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">processHprofFile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.552&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19781&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">thread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.575&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19780&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">analysis&lt;/span> &lt;span class="nx">dir&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.578&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19780&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mf">1.0.0&lt;/span>&lt;span class="nx">_2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span>&lt;span class="nx">_14&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">47_549&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hprof&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.600&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19780&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">active&lt;/span> &lt;span class="nx">thread&lt;/span> &lt;span class="nx">count&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">711&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.600&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19780&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">before&lt;/span> &lt;span class="nx">suspend&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">fork&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.684&lt;/span> &lt;span class="mi">19783&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19783&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">pid&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19783&lt;/span>                            &lt;span class="nx">I&lt;/span>  &lt;span class="nx">child&lt;/span> &lt;span class="nx">process&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">STARTED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">19783&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">06&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.752&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19780&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">notify&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="nx">pid&lt;/span> &lt;span class="mi">19783&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">06&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.752&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19780&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">wait&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">60070&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mo">06&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">47.753&lt;/span> &lt;span class="mi">18915&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">19780&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">end&lt;/span> &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">dump&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">ENDED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">19783&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 class="heading" id="父进程有正在运行线程fork前suspend子进程dump">
 父进程有正在运行线程，fork前suspend，子进程dump&lt;span class="heading__anchor"> &lt;a href="#%e7%88%b6%e8%bf%9b%e7%a8%8b%e6%9c%89%e6%ad%a3%e5%9c%a8%e8%bf%90%e8%a1%8c%e7%ba%bf%e7%a8%8bfork%e5%89%8dsuspend%e5%ad%90%e8%bf%9b%e7%a8%8bdump">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">24.453&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">20998&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nf">startLoop&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">29.986&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21049&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstAnalysisTime&lt;/span>&lt;span class="p">():&lt;/span>&lt;span class="mi">1709191024825&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">29.986&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21049&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getAnalysisTimes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">29.990&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21757&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">mTrackReasons&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="nx">reason_thread_oom&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">29.990&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21757&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dumpAndAnalysis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">29.999&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21758&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">processHprofFile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.000&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21758&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">thread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.015&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21757&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">analysis&lt;/span> &lt;span class="nx">dir&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.015&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21758&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">create&lt;/span> &lt;span class="nx">json&lt;/span> &lt;span class="nx">file&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">then&lt;/span> &lt;span class="nx">start&lt;/span> &lt;span class="nx">service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.018&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21757&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mf">1.0.0&lt;/span>&lt;span class="nx">_2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span>&lt;span class="nx">_14&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29_992&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hprof&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.018&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21758&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">file&lt;/span> &lt;span class="nx">size&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.036&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21757&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">active&lt;/span> &lt;span class="nx">thread&lt;/span> &lt;span class="nx">count&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">710&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.037&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21757&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">before&lt;/span> &lt;span class="nx">suspend&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">fork&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.147&lt;/span> &lt;span class="mi">21760&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21760&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">pid&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21760&lt;/span>                            &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">suspend&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">40&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.147&lt;/span> &lt;span class="mi">21760&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21760&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">pid&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21760&lt;/span>                            &lt;span class="nx">I&lt;/span>  &lt;span class="nx">child&lt;/span> &lt;span class="nx">process&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.148&lt;/span> &lt;span class="mi">21760&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21760&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">pid&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21760&lt;/span>                            &lt;span class="nx">I&lt;/span>  &lt;span class="nx">child&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">active&lt;/span> &lt;span class="nx">thread&lt;/span> &lt;span class="nx">count&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">710&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.148&lt;/span> &lt;span class="mi">21760&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21760&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">pid&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21760&lt;/span>                            &lt;span class="nx">I&lt;/span>  &lt;span class="nx">suspend&lt;/span> &lt;span class="nx">all&lt;/span> &lt;span class="nx">thread&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">spend&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">30.152&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21757&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">suspend&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">40&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">STARTED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">21760&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">32.005&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21757&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">notify&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="nx">pid&lt;/span> &lt;span class="mi">21760&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">32.006&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21757&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">wait&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1854&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">32.006&lt;/span> &lt;span class="mi">20998&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">21757&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">end&lt;/span> &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">dump&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">ENDED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">21760&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>结论: 子进程耗时没有卡住，dump的第一个步骤是suspend，长耗时是suspend造成的&lt;/p>
&lt;h3 class="heading" id="android-13">
 Android 13&lt;span class="heading__anchor"> &lt;a href="#android-13">#&lt;/a>&lt;/span>
&lt;/h3>&lt;h4 class="heading" id="alarm改成180">
 alarm改成180&lt;span class="heading__anchor"> &lt;a href="#alarm%e6%94%b9%e6%88%90180">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.568&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">7764&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nf">startLoop&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.506&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">7822&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstAnalysisTime&lt;/span>&lt;span class="p">():&lt;/span>&lt;span class="mi">1709191024825&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.506&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">7822&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getAnalysisTimes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.509&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">mTrackReasons&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="nx">reason_thread_oom&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.509&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dumpAndAnalysis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.512&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8535&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">processHprofFile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.516&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8535&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">thread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.525&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8535&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">old&lt;/span> &lt;span class="nx">files&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.540&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8535&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">fd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.777&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">analysis&lt;/span> &lt;span class="nx">dir&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.778&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mf">1.0.0&lt;/span>&lt;span class="nx">_2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29_17&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">34_512&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hprof&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.800&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">active&lt;/span> &lt;span class="nx">thread&lt;/span> &lt;span class="nx">count&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">712&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.801&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">before&lt;/span> &lt;span class="nx">suspend&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">fork&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">34.838&lt;/span>  &lt;span class="mi">8539&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8539&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">pid&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8539&lt;/span>                             &lt;span class="nx">I&lt;/span>  &lt;span class="nx">child&lt;/span> &lt;span class="nx">process&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">STARTED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">8539&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">29&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">39.794&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">t1&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nx">RUNNABLE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">29&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">39.794&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">t2&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nx">TERMINATED&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">29&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">39.794&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">t3&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nx">TERMINATED&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">29&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">39.795&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">notify&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="nx">pid&lt;/span> &lt;span class="mi">8539&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">29&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">39.795&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">wait&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">184958&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">02&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">29&lt;/span> &lt;span class="mi">17&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">29&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">39.795&lt;/span>  &lt;span class="mi">7764&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">8534&lt;/span>  &lt;span class="nx">OOMMonitor&lt;/span>              &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">end&lt;/span> &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">dump&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>结论: Android 13不fork没有子线程运行dump（KOOM fork前suspend方法不生效）&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:38.321 19711-19711 OOMMonitor               com.kwai.koom.demo                   I  startLoop()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.472 19711-19853 OOMMonitor               com.kwai.koom.demo                   I  OOMPreferenceManager.getFirstAnalysisTime():1709201524073
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.472 19711-19853 OOMMonitor               com.kwai.koom.demo                   I  OOMPreferenceManager.getAnalysisTimes:3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.473 19711-20567 OOMMonitor               com.kwai.koom.demo                   I  mTrackReasons:[reason_thread_oom]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.473 19711-20567 OOMMonitor               com.kwai.koom.demo                   I  dumpAndAnalysis
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.473 19711-20568 OOMMonitor               com.kwai.koom.demo                   I  processHprofFile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.473 19711-20568 OOMMonitor               com.kwai.koom.demo                   I  delete other version files thread
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.475 19711-20568 OOMMonitor               com.kwai.koom.demo                   I  create json file and then start service
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.478 19711-20568 OOMMonitor_...sisService com.kwai.koom.demo                   I  startAnalysisService
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.482 19711-20567 OOMMonitor               com.kwai.koom.demo                   I  hprof analysis dir:/storage/emulated/0/Android/data/com.kwai.koom.demo/files/performance/oom/memory/hprof-aly
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.482 19711-20567 OOMMonitor_...HeapDumper com.kwai.koom.demo                   I  dump /storage/emulated/0/Android/data/com.kwai.koom.demo/files/performance/oom/memory/hprof-aly/1.0.0_2024-03-01_10-24-43_473.hprof
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.485 19711-20568 OOMMonitor_...sisService com.kwai.koom.demo                   I  startAnalysisService get Pss:85973
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.485 19711-20567 OOMMonitor_...HeapDumper com.kwai.koom.demo                   I  parent process active thread count: 716
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:43.485 19711-20567 OOMMonitor_...HeapDumper com.kwai.koom.demo                   I  before suspend and fork.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:45.092 19711-20567 OOMMonitor_...HeapDumper com.kwai.koom.demo                   I  parent dump wait: 1607
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 10:24:45.093 19711-20567 OOMMonitor               com.kwai.koom.demo                   I  end hprof dump
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 class="heading" id="不fork有子线程运行dump">
 不fork有子线程运行dump&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%8dfork%e6%9c%89%e5%ad%90%e7%ba%bf%e7%a8%8b%e8%bf%90%e8%a1%8cdump">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:17.857  4939-4939  OOMMonitor               com.kwai.koom.demo                   I  startLoop()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:23.091  4939-5012  OOMMonitor               com.kwai.koom.demo                   I  OOMPreferenceManager.getFirstAnalysisTime():1709277563090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:23.091  4939-5012  OOMMonitor               com.kwai.koom.demo                   I  OOMPreferenceManager.getAnalysisTimes:0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:23.092  4939-5746  OOMMonitor               com.kwai.koom.demo                   I  mTrackReasons:[reason_thread_oom]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:23.092  4939-5746  OOMMonitor               com.kwai.koom.demo                   I  dumpAndAnalysis
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:23.093  4939-5747  OOMMonitor               com.kwai.koom.demo                   I  processHprofFile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:23.093  4939-5747  OOMMonitor               com.kwai.koom.demo                   I  delete other version files thread
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:23.100  4939-5746  OOMMonitor               com.kwai.koom.demo                   I  hprof analysis dir:/storage/emulated/0/Android/data/com.kwai.koom.demo/files/performance/oom/memory/hprof-aly
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:23.100  4939-5746  OOMMonitor_...HeapDumper com.kwai.koom.demo                   I  dump /storage/emulated/0/Android/data/com.kwai.koom.demo/files/performance/oom/memory/hprof-aly/1.0.0_2024-03-01_15-19-23_092.hprof
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:23.107  4939-5746  OOMMonitor_...HeapDumper com.kwai.koom.demo                   I  parent process active thread count: 716
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:24.924  4939-5746  OOMMonitor_...HeapDumper com.kwai.koom.demo                   I  dump spend time: 1817
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2024-03-01 15:19:24.925  4939-5746  OOMMonitor               com.kwai.koom.demo                   I  end hprof dump
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 class="heading" id="父进程没有其他线程运行时子进程dump">
 父进程没有其他线程运行时，子进程dump&lt;span class="heading__anchor"> &lt;a href="#%e7%88%b6%e8%bf%9b%e7%a8%8b%e6%b2%a1%e6%9c%89%e5%85%b6%e4%bb%96%e7%ba%bf%e7%a8%8b%e8%bf%90%e8%a1%8c%e6%97%b6%e5%ad%90%e8%bf%9b%e7%a8%8bdump">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">23.682&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">23437&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nf">startLoop&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.974&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">23527&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstAnalysisTime&lt;/span>&lt;span class="p">():&lt;/span>&lt;span class="mi">1709201524073&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.974&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">23527&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getAnalysisTimes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.976&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24239&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">mTrackReasons&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="nx">reason_thread_oom&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.976&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24239&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dumpAndAnalysis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.977&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24240&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">processHprofFile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.978&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24240&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">thread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.985&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24240&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">old&lt;/span> &lt;span class="nx">files&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.986&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24239&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">analysis&lt;/span> &lt;span class="nx">dir&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.986&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24239&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mf">1.0.0&lt;/span>&lt;span class="nx">_2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span>&lt;span class="nx">_10&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">28_977&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hprof&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.990&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24239&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">active&lt;/span> &lt;span class="nx">thread&lt;/span> &lt;span class="nx">count&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">716&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">28.990&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24239&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">before&lt;/span> &lt;span class="nx">suspend&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">fork&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">29.007&lt;/span> &lt;span class="mi">24241&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24241&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">pid&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24241&lt;/span>                            &lt;span class="nx">I&lt;/span>  &lt;span class="nx">child&lt;/span> &lt;span class="nx">process&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">29.009&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24240&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">fd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">STARTED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">24241&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">31.214&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24239&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">notify&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="nx">pid&lt;/span> &lt;span class="mi">24241&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">31.214&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24239&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">wait&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">2208&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">31.214&lt;/span> &lt;span class="mi">23437&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">24239&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">end&lt;/span> &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">dump&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">ENDED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">24241&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>结论: dump正常结果&lt;/p>
&lt;h4 class="heading" id="android13父进程有线程运行时子进程dump">
 Android13父进程有线程运行时，子进程dump&lt;span class="heading__anchor"> &lt;a href="#android13%e7%88%b6%e8%bf%9b%e7%a8%8b%e6%9c%89%e7%ba%bf%e7%a8%8b%e8%bf%90%e8%a1%8c%e6%97%b6%e5%ad%90%e8%bf%9b%e7%a8%8bdump">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">19.954&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">25380&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nf">startLoop&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.139&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">25433&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstAnalysisTime&lt;/span>&lt;span class="p">():&lt;/span>&lt;span class="mi">1709201524073&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.139&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">25433&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">OOMPreferenceManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getAnalysisTimes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.141&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26166&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">mTrackReasons&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="nx">reason_thread_oom&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.141&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26166&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dumpAndAnalysis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.142&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26167&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">processHprofFile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.142&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26167&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">delete&lt;/span> &lt;span class="nx">other&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="nx">thread&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.152&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26167&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">create&lt;/span> &lt;span class="nx">json&lt;/span> &lt;span class="nx">file&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">then&lt;/span> &lt;span class="nx">start&lt;/span> &lt;span class="nx">service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.154&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26166&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">analysis&lt;/span> &lt;span class="nx">dir&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.154&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26167&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">file&lt;/span> &lt;span class="nx">size&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.155&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26166&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="nx">storage&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">emulated&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Android&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">performance&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">oom&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">memory&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">hprof&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">aly&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mf">1.0.0&lt;/span>&lt;span class="nx">_2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span>&lt;span class="nx">_10&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">25_142&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hprof&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.156&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26167&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">last&lt;/span> &lt;span class="nx">analysis&lt;/span> &lt;span class="nx">isn&lt;/span>&lt;span class="err">&amp;#39;&lt;/span>&lt;span class="nx">t&lt;/span> &lt;span class="nx">succeed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">delete&lt;/span> &lt;span class="nx">file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.160&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26166&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">active&lt;/span> &lt;span class="nx">thread&lt;/span> &lt;span class="nx">count&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">717&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.160&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26166&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">before&lt;/span> &lt;span class="nx">suspend&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">fork&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">34&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.177&lt;/span> &lt;span class="mi">26169&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26169&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">pid&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26169&lt;/span>                            &lt;span class="nx">I&lt;/span>  &lt;span class="nx">child&lt;/span> &lt;span class="nx">process&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">STARTED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">26169&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">35&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.191&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26166&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">dump&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">notify&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="nx">pid&lt;/span> &lt;span class="mi">26169&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">35&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.191&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26166&lt;/span> &lt;span class="nx">OOMMonitor_&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="nx">HeapDumper&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">parent&lt;/span> &lt;span class="nx">process&lt;/span> &lt;span class="nx">wait&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">60015&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2024&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">03&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mo">01&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">35&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mf">25.191&lt;/span> &lt;span class="mi">25380&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">26166&lt;/span> &lt;span class="nx">OOMMonitor&lt;/span>               &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span>                   &lt;span class="nx">I&lt;/span>  &lt;span class="nx">end&lt;/span> &lt;span class="nx">hprof&lt;/span> &lt;span class="nx">dump&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">----------------------------&lt;/span> &lt;span class="nx">PROCESS&lt;/span> &lt;span class="nf">ENDED&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">26169&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kwai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">koom&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">demo&lt;/span> &lt;span class="o">----------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>结论: 子进程卡在dump&lt;/p></description></item><item><title>COW与HProf文件格式分析</title><link>https://opoojkk.com/posts/2024/11/02/cow-and-hprof/</link><pubDate>Sat, 02 Nov 2024 00:15:04 +0800</pubDate><guid>https://opoojkk.com/posts/2024/11/02/cow-and-hprof/</guid><description>&lt;h1 class="heading" id="cowcopy-on-write">
 COW(copy on write)&lt;span class="heading__anchor"> &lt;a href="#cowcopy-on-write">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>Linux通过fork()和exec()函数族创建新进程。&lt;/p>
&lt;h2 class="heading" id="为什么通过这种方式而不是从头开始创建新进程">
 为什么通过这种方式，而不是从头开始创建新进程？&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%80%9a%e8%bf%87%e8%bf%99%e7%a7%8d%e6%96%b9%e5%bc%8f%e8%80%8c%e4%b8%8d%e6%98%af%e4%bb%8e%e5%a4%b4%e5%bc%80%e5%a7%8b%e5%88%9b%e5%bb%ba%e6%96%b0%e8%bf%9b%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;blockquote>
&lt;p>Of course, one big question you might have: why would we build such an odd interface to what should be the simple act of creating a new process? Well, as it turns out, the separation of fork() and exec() is essential in building a UNIX shell, because it lets the shell run code after the call to fork() but before the call to exec(); this code can alter the environment of the about-to-be-run program, and thus enables a variety of interesting features to be readily built.
The shell is just a user program4 . It shows you a prompt and then waits for you to type something into it. You then type a command (i.e., the name of an executable program, plus any arguments) into it; in most cases, the shell then figures out where in the file system the executable resides, calls fork() to create a new child process to run the command, calls some variant of exec() to run the command, and then waits for the command to complete by calling wait(). When the child completes, the shell returns from wait() and prints out a prompt again, ready for your next command.
The separation of fork() and exec() allows the shell to do a whole bunch of useful things rather easily. For example:
prompt&amp;gt; wc p3.c &amp;gt; newfile.txt
In the example above, the output of the program wc is redirected into the output file newfile.txt (the greater-than sign is how said redirection is indicated). The way the shell accomplishes this task is quite simple: when the child is created, before calling exec(), the shell (specifically, the code executed in the child process) closes standard output and opens the file newfile.txt. By doing so, any output from the soonto-be-running program wc is sent to the file instead of the screen (open file descriptors are kept open across the exec() call, thus enabling this behavior [SR05]).&lt;/p>
&lt;/blockquote>
&lt;p>&amp;ndash;威斯康大学出的 Operating System: Three Easy Pieces 的第一章 第三节&lt;/p>
&lt;p>微软的研究&lt;/p>
&lt;p>fork()最早出现在Genie分时操作系统，允许父进程描述新进程的内存地址和机器上下文。默认情况下子进程和父进程共享内存，可以选择给子进程提供另一块完全不同的内存执行不同的程序。&lt;/p>
&lt;p>UNIX可以shell实现 prompt&amp;gt; wc p3.c &amp;gt; newfile.txt 这类功能。&lt;/p>
&lt;h2 class="heading" id="为什么用copy-on-write">
 为什么用Copy on Write&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e7%94%a8copy-on-write">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在Unix诞生早期，fork简单。不需要参数，为新进程提供了继承自父进程的默认值。后面随着进程的尺寸越来越大，需要复制文件锁、定时器、异步IO操作、跟踪等。此外，许多系统调用标志控制了fork在内存映射标志 MADV_DONTFORK/DOFORK/WIPEONFORK等）、文件描述符和线程都需要复制给子进程这些状态。操作耗时，且执行&lt;code>exec&lt;/code> 后会覆盖整个子进程的虚拟地址空间，浪费大量物理内存。因此才有了COW来减少这个开销。&lt;/p>
&lt;p>内核不会复制进程的整个地址空间，而是只复制其页表（即指向与父进程相同的内存页），&lt;code>fork&lt;/code> 之后的父子进程的地址空间指向同样的物理内存页。&lt;/p>
&lt;p>复制文件锁：访问共享文件时只有一个进程可以进程写操作&lt;/p>
&lt;p>定时器：确保每个进程都有机会执行&lt;/p>
&lt;p>异步IO操作：发起I/O请求的线程不等I/O操作完成，就继续执行随后的代码，I/O结果用其他方式通知发起I/O请求的程序&lt;/p>
&lt;p>跟踪：在进程切换过程中对进程状态和执行情况进行监控和记录，以便分析和调试&lt;/p>
&lt;h2 class="heading" id="copy-on-write机制">
 Copy on Write机制&lt;span class="heading__anchor"> &lt;a href="#copy-on-write%e6%9c%ba%e5%88%b6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>进程创建之后，操作系统分配一系列虚拟内存页，开始对所有可以访问内存页的进程标记为可读的，当一个进程尝试修改只读页的时候，操作系统创建一个独占的原始页的副本，其他的进程仍然指向原始的只读内存页。只有对副本的修改最对修改的一个进程可见。&lt;/p>
&lt;p>&lt;code>fork()&lt;/code> 之后，内核会把父进程的所有内存页都标记为&lt;strong>只读&lt;/strong>。一旦其中一个进程尝试写入某个内存页，就会触发一个保护故障（缺页异常），此时会陷入内核。&lt;/p>
&lt;p>内核拦截写入，并为尝试写入的进程创建这个页面的一个&lt;strong>新副本&lt;/strong>，恢复这个页面的&lt;strong>可写权限&lt;/strong>，然后重新执行这个写操作，这时就可以正常执行了。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/SsZzrVP.png" alt="image">&lt;/figure>&lt;p>内核会保留每个内存页面的引用数。每次复制某个页面后，该页面的引用数减少一；如果该页面只有一个引用，就可以跳过分配，直接修改。&lt;/p>
&lt;p>这种分配过程对于进程来说是透明的，能够确保一个进程的内存更改在另一进程中不可见。&lt;/p>
&lt;p>fork 之后可以不执行exec ，直接执行其他方法&lt;/p>
&lt;h2 class="heading" id="koom-copy-on-write">
 KOOM Copy on Write&lt;span class="heading__anchor"> &lt;a href="#koom-copy-on-write">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Linux SuspendAll方法不会让执行SuspendAll的线程suspend（保留着一个线程）&lt;/p>
&lt;p>父进程等待子进程执行完成&lt;/p>
&lt;h2 class="heading" id="为什么先suspend再fork">
 为什么先suspend再fork&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e5%85%88suspend%e5%86%8dfork">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>如果去掉 &lt;code>fork()&lt;/code> 前额外添加的虚拟机暂停请求，直接让 &lt;code>Debug.dumpHprofData()&lt;/code> 在子进程暂停虚拟机线程，会发现暂停虚拟机线程的调用将被一直阻塞。&lt;/p>
&lt;p>因为 fork() 得出的子进程仅会保留调用 &lt;code>fork()&lt;/code> 的唯一线程，而虚拟机线程暂停需要当前线程等待其它线程到达 Suspend Check Point 以通知原线程。但子进程中的其它线程已经不复存在了，等待便不会再有任何回音，因此通过在原进程提前暂停虚拟机线程，欺骗子进程对虚拟机线程状态的检测，才能保证逻辑的正常运行。&lt;/p>
&lt;h1 class="heading" id="hprof">
 hprof&lt;span class="heading__anchor"> &lt;a href="#hprof">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>Hprof文件格式有明确组织方式，Android在Java的基础上新增了部分Tag。&lt;/p>
&lt;h2 class="heading" id="java-hprof格式">
 Java Hprof格式&lt;span class="heading__anchor"> &lt;a href="#java-hprof%e6%a0%bc%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;img src="https://i.imgur.com/TO3pfaS.png" alt="image">
&lt;a href="https://leo-wxy.github.io/images/Java%20Hprof%E6%A0%BC%E5%BC%8F.png">Java Hprof格式&lt;/a>&lt;/p>
&lt;p>整体分为&lt;code>Header&lt;/code>和&lt;code>Record数组&lt;/code>两部分。&lt;/p>
&lt;h3 class="heading" id="header">
 Header&lt;span class="heading__anchor"> &lt;a href="#header">#&lt;/a>&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>记录hprof的元信息
&lt;img src="https://i.imgur.com/T5nCJAp.png" alt="image">
&lt;a href="https://leo-wxy.github.io/images/Hprof-Header">Hprof-Header&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>
&lt;p>格式名和版本号：JAVA PROFILE 1.0.2 (&lt;code>18byte&lt;/code>)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>标识符大小：&lt;code>4byte&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>高位时间戳：&lt;code>4byte&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>地位时间戳：&lt;code>4byte&lt;/code>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>&lt;code>Header&lt;/code>总共占了&lt;code>18 + 4 + 4 + 4 = 32byte&lt;/code>&lt;/p>
&lt;h3 class="heading" id="record数组">
 Record数组&lt;span class="heading__anchor"> &lt;a href="#record%e6%95%b0%e7%bb%84">#&lt;/a>&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>记录各个类型对应的数据
&lt;img src="https://i.imgur.com/fDcf4tv.png" alt="image">
&lt;a href="https://leo-wxy.github.io/images/Hprof-Record">Hprof-Record&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>类型(&lt;/strong>&lt;code>TAG&lt;/code>&lt;strong>)&lt;/strong>：表示Record对应的类型(&lt;code>1Byte&lt;/code>)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>时间戳(&lt;code>TIME&lt;/code>)：发生时间(&lt;code>4byte&lt;/code>)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>长度(&lt;code>LENGTH&lt;/code>)：记录数据的长度(&lt;code>4byte&lt;/code>)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>记录数据(&lt;code>BODY&lt;/code>)：记录的数据(&lt;code>${LENGTH}Byte&lt;/code>)&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>单条&lt;code>Record&lt;/code>总共占了&lt;code>1 + 4 + 4 + LENGTH byte&lt;/code>&lt;/p>
&lt;h3 class="heading" id="支持的tag类型">
 支持的TAG类型&lt;span class="heading__anchor"> &lt;a href="#%e6%94%af%e6%8c%81%e7%9a%84tag%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/span>
&lt;/h3>&lt;h4 class="heading" id="一级tag">
 一级Tag&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%80%e7%ba%a7tag">#&lt;/a>&lt;/span>
&lt;/h4>&lt;ul>
&lt;li>
&lt;p>STRING_IN_UTF8 = 0x01&lt;/p>
&lt;/li>
&lt;li>
&lt;p>LOAD_CLASS = 0x02&lt;/p>
&lt;/li>
&lt;li>
&lt;p>UNLOAD_CLASS = 0x03&lt;/p>
&lt;/li>
&lt;li>
&lt;p>STACK_FRAME = 0x04&lt;/p>
&lt;/li>
&lt;li>
&lt;p>STACK_TRACE = 0x05&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ALLOC_SITES = 0x06&lt;/p>
&lt;/li>
&lt;li>
&lt;p>HEAP_SUMMARY = 0x07&lt;/p>
&lt;/li>
&lt;li>
&lt;p>START_THREAD = 0x0a&lt;/p>
&lt;/li>
&lt;li>
&lt;p>END_THREAD = 0x0b&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>HEAP_DUMP = 0x0c - 堆内容&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CPU_SAMPLES = 0x0d&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CONTROL_SETTINGS = 0x0e&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>HEAP_DUMP_SEGMENT = 0x1c - 堆内容&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>HEAP_DUMP_END = 0x2c&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h4 class="heading" id="二级tag">
 二级Tag&lt;span class="heading__anchor"> &lt;a href="#%e4%ba%8c%e7%ba%a7tag">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>主要位于&lt;code>HEAP_DUMP&lt;/code>或&lt;code>HEAP_DUMP_SEGMENT&lt;/code>中&lt;/p>
&lt;ul>
&lt;li>
&lt;p>ROOT_UNKNOWN = 0xff&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_JNI_GLOBAL = 0x01&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_JNI_LOCAL = 0x02&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_JAVA_FRAME = 0x03&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_NATIVE_STACK = 0x04&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_STICKY_CLASS = 0x05&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_THREAD_BLOCK = 0x06&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_MONITOR_USED = 0x07&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_THREAD_OBJECT = 0x08&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>CLASS_DUMP = 0x20&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>INSTANCE_DUMP = 0x21&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>OBJECT_ARRAY_DUMP = 0x22&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>PRIMITIVE_ARRAY_DUMP = 0x23&lt;/strong>：占据到80%以上&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="android-hprof格式">
 Android Hprof格式&lt;span class="heading__anchor"> &lt;a href="#android-hprof%e6%a0%bc%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;img src="https://i.imgur.com/7ltPj6M.png" alt="image">
&lt;a href="https://leo-wxy.github.io/images/Android%20Hprof%E6%A0%BC%E5%BC%8F.png">Android Hprof格式&lt;/a>&lt;/p>
&lt;h3 class="heading" id="header-1">
 Header&lt;span class="heading__anchor"> &lt;a href="#header-1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>格式与Java的Header一致&lt;/p>
&lt;h3 class="heading" id="record数组-1">
 Record数组&lt;span class="heading__anchor"> &lt;a href="#record%e6%95%b0%e7%bb%84-1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>格式与Java的Record一致&lt;/p>
&lt;h3 class="heading" id="支持的tag类型-1">
 支持的TAG类型&lt;span class="heading__anchor"> &lt;a href="#%e6%94%af%e6%8c%81%e7%9a%84tag%e7%b1%bb%e5%9e%8b-1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;h4 class="heading" id="一级tag-1">
 一级Tag&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%80%e7%ba%a7tag-1">#&lt;/a>&lt;/span>
&lt;/h4>&lt;ul>
&lt;li>
&lt;p>STRING_IN_UTF8 = 0x01&lt;/p>
&lt;/li>
&lt;li>
&lt;p>LOAD_CLASS = 0x02&lt;/p>
&lt;/li>
&lt;li>
&lt;p>STACK_FRAME = 0x04&lt;/p>
&lt;/li>
&lt;li>
&lt;p>STACK_TRACE = 0x05&lt;/p>
&lt;/li>
&lt;li>
&lt;p>HEAP_DUMP_SEGMENT = 0x1c&lt;/p>
&lt;/li>
&lt;li>
&lt;p>HEAP_DUMP_END = 0x2c&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h4 class="heading" id="二级tag-1">
 二级Tag&lt;span class="heading__anchor"> &lt;a href="#%e4%ba%8c%e7%ba%a7tag-1">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>主要位于&lt;code>HEAP_DUMP_SEGMENT&lt;/code>中&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Java所有的二级Tag&lt;/p>
&lt;/li>
&lt;li>
&lt;p>HEAP_DUMP_INFO = 0xfe：存储的是&lt;code>堆空间的类型&lt;/code>，主要有以下三种&lt;/p>
&lt;ul>
&lt;li>
&lt;p>HEAP_ZYGOTE = 0x5A &lt;code>Z&lt;/code> 系统堆空间&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>HEAP_APP&lt;/strong> = 0x41 &lt;code>A&lt;/code> 应用堆空间&lt;/p>
&lt;/li>
&lt;li>
&lt;p>HEAP_IMAGE = 0x49 &lt;code>I&lt;/code> 图片堆空间&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>ROOT_INTERNED_STRING = 0x89&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_FINALIZING = 0x8a&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_DEBUGGER = 0x8b&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_REFERENCE_CLEANUP = 0x8c&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_VM_INTERNAL = 0x8d&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_JNI_MONITOR = 0x8e&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ROOT_UNREACHABLE = 0x90&lt;/p>
&lt;/li>
&lt;li>
&lt;p>PRIMITIVE_ARRAY_NODATA = 0xc3&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="heading">
 &lt;span class="heading__anchor"> &lt;a href="#heading">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h2 class="heading" id="完整android-hprof">
 完整Android hprof&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%8c%e6%95%b4android-hprof">#&lt;/a>&lt;/span>
&lt;/h2>&lt;figure>
 &lt;img src="https://i.imgur.com/tIB3MGb.jpeg" alt="image">&lt;/figure>&lt;!-- 完整C++代码: -->
&lt;!-- # KOOM裁剪

![image](https://i.imgur.com/Xj1K7R4.png)
需要裁剪掉全部基本类型数组的值，例如`char[](字符串)、byte[](图片)`，在处理`内存泄漏`相关问题时，一般也只关心`对象间的引用以及对象大小`，裁剪掉一些消息不会影响分析。

保证基本Hprof文件功能：

* 只对`HEAP_DUMP_SEGMENT`的`Record`下进行裁剪，其他保持不变。例如`STRING、LOAD_CLASS`等

* 在`HEAP_DUMP_SEGMENT`的`Record`下，主要删除`PRIMITIVE_ARRAY_DUMP`，这一块主要占用80%的内容

在裁剪`INSTANCE_DUMP(实例)`、`OBJECT_ARRAY_DUMP(对象数组)`，`CLASS_DUMP(类或接口)`和`HEAP_DUMP_INFO(记录当前堆位置)`时需要再去掉`Zygote Heap`和`Image Heap`

* 主要通过判断`HEAP_DUMP_INFO`的`heapType`是否为`HEAP_ZYGOTE(Z)`和`HEAP_IMAGE(I)`

## 

Android heap:

* **image heap**: The system boot image, containing classes that are preloaded during boot time. Allocations here are guaranteed to never move or go away.

* **zygote heap**: The copy-on-write heap where an app process is forked from in the Android system.

* **app heap**: The primary heap on which your app allocates memory.

* **default heap**: When no heap is specified by the system.

* **JNI heap**: The heap that shows where Java Native Interface (JNI) references are allocated and released.

dump出的hprof只包含前三种。


# KOOM

谷歌从Android 7.0开始对调用系统库做了限制，快手的kwai-linker组件绕过了限制。


只支持到Android12, Google在Android13对fork的改动大。 --></description></item><item><title>查身份证的权力</title><link>https://opoojkk.com/posts/2024/11/01/%E6%9F%A5%E8%BA%AB%E4%BB%BD%E8%AF%81%E7%9A%84%E6%9D%83%E5%8A%9B/</link><pubDate>Fri, 01 Nov 2024 21:00:01 +0800</pubDate><guid>https://opoojkk.com/posts/2024/11/01/%E6%9F%A5%E8%BA%AB%E4%BB%BD%E8%AF%81%E7%9A%84%E6%9D%83%E5%8A%9B/</guid><description>&lt;h1 class="heading" id="查身份的权力">
 查身份的权力&lt;span class="heading__anchor"> &lt;a href="#%e6%9f%a5%e8%ba%ab%e4%bb%bd%e7%9a%84%e6%9d%83%e5%8a%9b">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>今日周五, 回家路上心情尚可. 快到小区门口时见到红蓝灯交互闪动, 心想不妙: 又他妈要查身份证. 走到门口时, 果不其然. 心中即使有不甘心仍报上身份证号码.&lt;/p>
&lt;p>从小区门口走到卧室的途中越想越气. 遂查阅一番查阅身份相关规定.如下:&lt;/p>
&lt;p>&lt;a href="https://www.gov.cn/banshi/2005-08/02/content_19457.htm">中华人民共和国居民身份证法&lt;/a>&lt;/p>
&lt;p>第十五条　人民警察依法执行职务，遇有下列情形之一的，经出示执法证件，可以查验居民身份证：&lt;/p>
&lt;p>　　（一）对有违法犯罪嫌疑的人员，需要查明身份的；&lt;/p>
&lt;p>　　（二）依法实施现场管制时，需要查明有关人员身份的；&lt;/p>
&lt;p>　　（三）发生严重危害社会治安突发事件时，需要查明现场有关人员身份的；&lt;/p>
&lt;p>　　（四）法律规定需要查明身份的其他情形。&lt;/p>
&lt;p>　　有前款所列情形之一，拒绝人民警察查验居民身份证的，依照有关法律规定，分别不同情形，采取措施予以处理。&lt;/p>
&lt;p>　　任何组织或者个人不得扣押居民身份证。但是，公安机关依照《中华人民共和国刑事诉讼法》执行监视居住强制措施的情形除外。&lt;/p>
&lt;p>‍&lt;/p>
&lt;p>&lt;a href="https://www.gov.cn/flfg/2005-08/29/content_27130.htm">中华人民共和国主席令 第 三十八 号&lt;/a>&lt;/p>
&lt;pre>&lt;code>**第五十六条**　旅馆业的工作人员对住宿的旅客不按规定登记姓名、身份证件种类和号码的，或者明知住宿的旅客将危险物质带入旅馆，不予制止的，处二百元以上五百元以下罚款。 
&lt;/code>&lt;/pre>
&lt;p>　　旅馆业的工作人员明知住宿的旅客是犯罪嫌疑人员或者被公安机关通缉的人员，不向公安机关报告的，处二百元以上五百元以下罚款；情节严重的，处五日以下拘留，可以并处五百元以下罚款。&lt;br>
　　&lt;strong>第五十七条&lt;/strong>　房屋出租人将房屋出租给无身份证件的人居住的，或者不按规定登记承租人姓名、身份证件种类和号码的，处二百元以上五百元以下罚款。&lt;br>
　　房屋出租人明知承租人利用出租房屋进行犯罪活动，不向公安机关报告的，处二百元以上五百元以下罚款；情节严重的，处五日以下拘留，可以并处五百元以下罚款。&lt;/p>
&lt;p>‍&lt;/p>
&lt;p>&lt;a href="https://www.12389.gov.cn/zhrmghgrmjcf.html">中华人民共和国人民警察法&lt;/a>&lt;/p>
&lt;p>&lt;strong>第九条&lt;/strong> 为维护社会治安秩序，公安机关的人民警察对有违法犯罪嫌疑的人员，经出示相应证件，可以当场盘问、检查；经盘问、检查，有下列情形之一的，可以将其带至公安机关，经该公安机关批准，对其继续盘问：&lt;/p>
&lt;p>（一）被指控有犯罪行为的；&lt;/p>
&lt;p>（二）有现场作案嫌疑的；&lt;/p>
&lt;p>（三）有作案嫌疑身份不明的；&lt;/p>
&lt;p>（四）携带的物品有可能是赃物的。&lt;/p>
&lt;p>对被盘问人的留置时间自带至公安机关之时起不超过二十四小时，在特殊情况下，经县级以上公安机关批准，可以延长至四十八小时，并应当留有盘问记录。对于批准继续盘问的，应当立即通知其家属或者其所在单位。对于不批准继续盘问的，应当立即释放被盘问人。&lt;/p>
&lt;p>经继续盘问，公安机关认为对被盘问人需要依法采取拘留或者其他强制措施的，应当在前款规定的期间作出决定；在前款规定的期间不能作出上述决定的，应当立即释放被盘问人。&lt;/p>
&lt;p>‍&lt;/p>
&lt;p>得出结论: 警察想查就能查. 放弃幻想.&lt;/p></description></item><item><title>删除.gitignore中的文件</title><link>https://opoojkk.com/posts/2024/10/27/%E5%88%A0%E9%99%A4.gitignore%E4%B8%AD%E7%9A%84%E6%96%87%E4%BB%B6/</link><pubDate>Sun, 27 Oct 2024 13:52:19 +0800</pubDate><guid>https://opoojkk.com/posts/2024/10/27/%E5%88%A0%E9%99%A4.gitignore%E4%B8%AD%E7%9A%84%E6%96%87%E4%BB%B6/</guid><description>&lt;p>.gitignore文件中可以列举出哪些文件不希望提交到git中, 只对还没有提交的文件生效, 如果已经有文件提交了, 即使添加到.gitignore也不会起作用.这时候可以用命令:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git rm -r --cached file/directory
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h1 class="heading" id="参数含义">
 参数含义&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e6%95%b0%e5%90%ab%e4%b9%89">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>rm:
Remove files from the working tree and from the index.&lt;/p>
&lt;p>-r:
allow recursive removal&lt;/p>
&lt;p>&amp;ndash;cached:
only remove from the index&lt;/p></description></item><item><title>京东 杨笠 梁文道</title><link>https://opoojkk.com/posts/2024/10/25/%E4%BA%AC%E4%B8%9C-%E6%9D%A8%E7%AC%A0-%E6%A2%81%E6%96%87%E9%81%93/</link><pubDate>Fri, 25 Oct 2024 09:00:06 +0800</pubDate><guid>https://opoojkk.com/posts/2024/10/25/%E4%BA%AC%E4%B8%9C-%E6%9D%A8%E7%AC%A0-%E6%A2%81%E6%96%87%E9%81%93/</guid><description>&lt;p>从我开始听&amp;quot;八分&amp;quot;这个播客节目开始就很喜欢梁文道, 原因是因为梁文道有学识, 谦虚, 又经常在节目中讨论一些观点, 绝大多数时清况下, 我对讨论的事件和观点都很感兴趣, 有许多是我没有想到过的角度.&lt;/p>
&lt;p>但是, 凡事都有个但是, 最近有关京东和杨笠的一期, 实在让我不能同意.
首先, 道长(梁文道昵称)的观点认为杨笠冒犯的男士是台上一期演出的男嘉宾, 认为&amp;quot;普信男&amp;quot;说的特制这几个人, 是不是这几个人呢?我找来了杨笠的原话,是这样的说的:&lt;/p>
&lt;p>&amp;ldquo;大家好, 我是杨笠, 我又来了, 跟去年一样, 我还是单身. 就是我也单身太久了吧, 我觉着自从做了这一行以后, 对我的感情生活是可以说是雪上加霜, 我真的是不懂, 我为什么会单身这么久, 我觉着很多男生可能都有点误会我了, 他们就觉得, 我天天在台上讲段子, 感觉很凶, 好像很喜欢攻击男生, 我是不是讨厌男生. 没有, 恰恰相反, 我是因为喜欢你们才攻击你们的. 真的, 我太喜欢你们了, 我喜欢你们喜欢到我都不忍心谈恋爱, 因为我没有办法在众多的你们当中选出一个. 我会想说, 像男人这么美好的东西竟然还要选. 男人不光美好, 还特别神秘, 就是你永远也猜不透, 他那小脑瓜里到底在想些什么, 就是他明明为什么看起来那么普通但是他却可以那么自信. 就是你在这个台上, 看到很多很奇形怪状的男演员, 但是我跟你说, 他们其中没有任何一个想过说我这个长相要是上电视的话是不是得稍微整容, 没有的&amp;hellip;.&amp;rdquo;&lt;/p>
&lt;p>在舞台上之前的描述说的群体是所有男性, 更具体一些, 根据语境可以说是单身男性. 后面说完&amp;quot;普信男&amp;quot;之后描述了场上的男嘉宾. 前后两句的主体是不一样的, 可以有两种理解:1. 后一句话是前一句的举例; 2. 从头到尾都是在说场上的男嘉宾.&lt;/p>
&lt;p>如果开始就是要说在场的男嘉宾, 显然不是要从在场的男嘉宾中找男朋友, 那开始的一句话的语境就是不对的. 所以认为后一句话是举例.
按照这样理解, &amp;ldquo;普通且自信的&amp;quot;自然就是泛指所有男性了. 感到被冒犯. 并不是因为什么认同, 我也不会觉着这个群体有多么好, 多么伟大, 单纯从我自身出发, 感到被冒犯, 没有被尊重.&lt;/p>
&lt;p>再说&amp;quot;脱口秀&amp;rdquo;, 总有人说&amp;quot;脱口秀&amp;quot;是&amp;quot;冒犯的艺术&amp;quot;, 冒犯是从什么时候开始和艺术联系在一起的呢? 又是为什么可以冒犯其他人? 我不认为这个这个正常的, 亦不能认同. 也不存在所谓的强者是不在意冒犯的, 是不是强者从来不是能不能被冒犯的标准和原因.&lt;/p>
&lt;p>有人说你破防了(破防了肤浅的指在意了)就是说你, 这样的说法很可笑, 我骂一句什么样的人或者那里的人, 其中你是其中一个, 并附带上谁破防了就是谁,你能同意吗? 不过是为自己收到的不公找一个发泄的出口罢了, 理所当然的认为都是这样.&lt;/p>
&lt;p>也不需要讨论诸如怎么别人不在意, 别人不在意是别人的事, 我在意.&lt;/p>
&lt;p>我从来不为女性的处境辩解, 有很大一部分是因为男性, 又有一部分是因为女性. 男性更多时候握有更大的权力, 身处更高的位置, 这可以是让女性处在劣势的原因之一. 整个社会包括女性自己, 认为女性就要怎么怎么样, 诸如化妆之类的, 也可以是让女性处在劣势的原因之一. 前提是真的是因为性别是女性而早遭到了不公的待遇, 而不是因为别的原因. 明明是自己的原因还动不动拿自己是女性, 怎么你们都怎么怎么这种做法让我厌恶.&lt;/p>
&lt;p>我总是认为无论性别, 长相, 我们都有相同的权利. 在追求, 兴趣, 爱好等等方面, 有许许多多女性做的比男性好, 这也没什么. 除了性别, 我们都是相同的, 一样是人.&lt;/p></description></item><item><title>国籍与文化认同</title><link>https://opoojkk.com/posts/2024/09/30/%E5%9B%BD%E7%B1%8D%E4%B8%8E%E6%96%87%E5%8C%96%E8%AE%A4%E5%90%8C/</link><pubDate>Mon, 30 Sep 2024 19:27:32 +0800</pubDate><guid>https://opoojkk.com/posts/2024/09/30/%E5%9B%BD%E7%B1%8D%E4%B8%8E%E6%96%87%E5%8C%96%E8%AE%A4%E5%90%8C/</guid><description>&lt;p>在这之前粗浅的认为一个人国籍就是一个人的符号，因此常常对“美籍华人、日籍华人”这样类似的说法感到厌恶，&lt;a href="https://www.youtube.com/watch?v=zqazKoDZCBg">[ENG SUB] 曾经的“猪仔”，马来西亚华人现在过得怎么样？【食贫道】&lt;/a>触动了我一下。&lt;/p>
&lt;p>除了一个人在法律上的身份(也就是国籍)之外，背后认同的文化也是这个人的一部分。一个是法律上的定义，一个是思想上的归属。文化甚至是可以与国籍分庭抗礼的。&lt;/p></description></item><item><title>Android扩大点击区域</title><link>https://opoojkk.com/posts/2024/09/08/android%E6%89%A9%E5%A4%A7%E7%82%B9%E5%87%BB%E5%8C%BA%E5%9F%9F/</link><pubDate>Sun, 08 Sep 2024 10:28:48 +0800</pubDate><guid>https://opoojkk.com/posts/2024/09/08/android%E6%89%A9%E5%A4%A7%E7%82%B9%E5%87%BB%E5%8C%BA%E5%9F%9F/</guid><description>&lt;p>Android中常用扩大点击区域的方式是为View添加padding, 并重新计算View的尺寸.
Android中提供了另一种方式: TouchDelegate&lt;/p>
&lt;h2 class="heading" id="touchdelegate">
 TouchDelegate&lt;span class="heading__anchor"> &lt;a href="#touchdelegate">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Android TouchDelegate is a powerful tool that helps developers create a custom touch region for a View. This allows developers to extend the area of the View that responds to touch events. For example, if a View is small, developers can use TouchDelegate to increase the area that responds to touch events. This makes it easier to interact with the View and makes the user experience more seamless. The TouchDelegate class also helps developers create custom touch feedback, such as a ripple effect, when the user interacts with a View. This can make the user experience more intuitive and engaging.&lt;/p>
&lt;h3 class="heading" id="用法">
 用法&lt;span class="heading__anchor"> &lt;a href="#%e7%94%a8%e6%b3%95">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="p">((&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getParent&lt;/span>&lt;span class="p">()).&lt;/span>&lt;span class="na">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">run&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Rect&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">bounds&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Rect&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setEnabled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getHitRect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bounds&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">bounds&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">top&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">top&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">bounds&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">bottom&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">bottom&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">bounds&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">left&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">left&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">bounds&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">right&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">right&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">TouchDelegate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">touchDelegate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TouchDelegate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bounds&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getParent&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">instanceof&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getParent&lt;/span>&lt;span class="p">()).&lt;/span>&lt;span class="na">setTouchDelegate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">touchDelegate&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="原理">
 原理&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ol>
&lt;li>为什么可以扩大区域？
当View中有点击事件时首先交给TouchDelegate判断，是否在它扩大后的Rect范围内，在范围内把点击的坐标映射到target范围内后，按照正常的事件分发处理。&lt;/li>
&lt;li>为什么需要写在父类中？
如果在子View中写扩大子View的范围，扩大的范围甚至都不在子View的范围，怎么实现扩大呢。&lt;/li>
&lt;li>&lt;del>TouchDelegate中滑动判断？&lt;/del>&lt;/li>
&lt;li>怎么避免被代理的子View重复处理事件？
先判断TouchDelegate是否消费，若消费事件分发完成，不消费按照正常的流程。&lt;/li>
&lt;/ol>
&lt;p>需要注意的是一个父View中只能为一个子View设置TouchDelegate= =|||&lt;/p></description></item><item><title>Android快捷方式开发指南</title><link>https://opoojkk.com/posts/2024/09/08/android%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97/</link><pubDate>Sun, 08 Sep 2024 10:02:20 +0800</pubDate><guid>https://opoojkk.com/posts/2024/09/08/android%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97/</guid><description>&lt;h2 class="heading" id="android-快捷方式">
 Android 快捷方式&lt;span class="heading__anchor"> &lt;a href="#android-%e5%bf%ab%e6%8d%b7%e6%96%b9%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>在Android系统中，快捷方式是一种非常方便的工具，可以帮助用户更快速地访问他们经常使用的应用程序或功能。用户可以在主屏幕或应用程序抽屉中创建快捷方式，并可自定义快捷方式的名称和图标。&lt;/p>
&lt;p>要在主屏幕上创建快捷方式，用户可以长按应用程序图标，然后将其拖到主屏幕上。用户还可以在主屏幕上空白的位置上长按，然后选择“快捷方式”菜单，以创建快捷方式。在应用程序抽屉中，用户可以长按应用程序图标，然后将其拖到主屏幕上以创建快捷方式。&lt;/p>
&lt;p>除了常规的应用程序快捷方式外，用户还可以创建联系人、网站、设置选项等其他类型的快捷方式。例如，用户可以创建一个联系人快捷方式，这样他们就可以快速拨打某个联系人的电话号码。用户还可以创建一个网站快捷方式，以便更快地访问他们最喜欢的网站。&lt;/p>
&lt;p>总之，Android快捷方式是一种非常有用的工具，可以帮助用户更快速地访问他们经常使用的应用程序、联系人、网站等。通过利用快捷方式，用户可以更快地完成任务并提高他们的生产力。&lt;/p>
&lt;blockquote>
&lt;p>以上内容通过Notion AI生成，没有什么卵用。&lt;/p>
&lt;/blockquote>
&lt;p>原生Android是抽屉的形式，打开抽屉才是所有的APP，桌面上显示的Icon都叫快捷方式，除了常规的把APP从抽屉拖到桌面这种形式，APP体统的shortcut也是可以创建一个单独的Icon，也成为快捷方式。但中国大陆的launcher普遍都是定制的，没有抽屉，更没有所谓的快捷方式。还是尝试一下.&lt;/p>
&lt;p>今天主要试一下添加Shortcut，以下是具体内容：&lt;/p>
&lt;h2 class="heading" id="shortcut">
 Shortcut&lt;span class="heading__anchor"> &lt;a href="#shortcut">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>shortcut是对标iOS上的xx(忘了叫啥)。好处能让用户更快进到想看的Activity。&lt;/p>
&lt;h3 class="heading" id="注册方式">
 注册方式&lt;span class="heading__anchor"> &lt;a href="#%e6%b3%a8%e5%86%8c%e6%96%b9%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>静态注册&lt;br>
在AndroidManifest中的MainActivity（真正意义上的，启动后第一个Activity）中引入写好的xml文件，文件中以shortcuts作为根节点。不可以通过&lt;code>ShortcutManager&lt;/code>​操作静态方式添加的shortcut，会抛出异常。&lt;/p>
&lt;blockquote>
&lt;p>提示shortcuts找不到声明是正常现象，不影响内部元素提示😒。&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;li>
&lt;p>|动态注册|||
|在Activity中通过&lt;code>ShortcutManager&lt;/code>​类添加、移除、禁用shortcut，没有限制一定要在MainActivity。id相同时后面的shortcut会覆盖先添加的。|||
|动态注册的API通过建造者模式，传递数据也比方便。|||&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>&lt;/th>
 &lt;th>静态注册&lt;/th>
 &lt;th>动态注册&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>实现方式&lt;/td>
 &lt;td>在 AndroidManifest.xml 文件中引用写好的 xml 文件&lt;/td>
 &lt;td>通过 ShortcutManager.addDynamicShortcuts()、ShortcutManager.updateShortcuts()、ShortcutManager.removeDynamicShortcuts() 以及 ShortcutManager.removeAllDynamicShortcuts() 方法添加、更新、移除或禁用动态快捷方式&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>是否支持动态修改&lt;/td>
 &lt;td>不支持&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>是否支持读取快捷方式&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>是否支持监听快捷方式&lt;/td>
 &lt;td>不支持&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>是否支持多个快捷方式&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>应用启动时是否可用&lt;/td>
 &lt;td>是&lt;/td>
 &lt;td>否&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;blockquote>
&lt;p>💡 无论哪种方式，shortcutId和shortcutShortLabel都是必须的。且都要设置Action为&lt;code>&amp;quot;android.intent.action.VIEW&amp;quot;&lt;/code>​&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;/ol></description></item><item><title>Android FFmpeg编译实战</title><link>https://opoojkk.com/posts/2024/09/08/android-ffmpeg%E7%BC%96%E8%AF%91%E5%AE%9E%E6%88%98/</link><pubDate>Sun, 08 Sep 2024 09:54:14 +0800</pubDate><guid>https://opoojkk.com/posts/2024/09/08/android-ffmpeg%E7%BC%96%E8%AF%91%E5%AE%9E%E6%88%98/</guid><description>&lt;p>近来学习音视频, ffmpeg又是音视频中绕不开的,于是编译ffmpeg.
试了好多天, 总算摸出点规律, 记录下来给需要的朋友提供一点线索.&lt;/p>
&lt;h2 class="heading" id="编译">
 编译&lt;span class="heading__anchor"> &lt;a href="#%e7%bc%96%e8%af%91">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ol>
&lt;li>从ffmpeg仓库克隆源码&lt;/li>
&lt;li>安装ndk&lt;/li>
&lt;li>执行编译脚本&lt;/li>
&lt;/ol>
&lt;h3 class="heading" id="sysroot">
 sysroot&lt;span class="heading__anchor"> &lt;a href="#sysroot">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>​&lt;code>sysroot&lt;/code>​被称为逻辑根目录，只在链接过程中起作用，作为交叉编译工具链搜索库文件的根路径，如配置&lt;code>--sysroot=dir&lt;/code>​，则&lt;code>dir&lt;/code>​作为逻辑根目录，链接器将在&lt;code>dir/usr/lib&lt;/code>​中搜索库文件。&lt;/p>
&lt;h3 class="heading" id="脚本">
 脚本&lt;span class="heading__anchor"> &lt;a href="#%e8%84%9a%e6%9c%ac">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>ndk路径的&lt;span data-type="text">tips：&lt;/span>&lt;/p>
&lt;p>&lt;span data-type="text">打开Android Studio中SDK Manager，进到Android SDK Location文件夹下&lt;/span>&lt;/p>
&lt;p>再进到ndk文件夹，里面是你已经下载好的ndk，进到一个版本中，就是ndk的路径。&lt;/p>
&lt;p>‍&lt;/p>
&lt;p>建议将脚本保存到克隆下来的ffmpeg仓库路径中.&lt;/p>
&lt;p>脚本代码：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># NDK的路径 (在bash.rc、zshrc中设置的路径，其中的NDK_HOME需要配置环境变量，如果你不配，在这改成你自己的也可以。)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">NDK_HOME&lt;/span>&lt;span class="o">=&lt;/span>~/Library/Android/sdk/ndk/25.1.8937393
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># NDK_ROOT=你的NDK路径&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">TOOLCHAIN_PREFIX&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$NDK_HOME&lt;/span>/toolchains/llvm/prebuilt/darwin-x86_64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; FFMPEG 交叉编译 &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; 基于当前系统NDK地址： &lt;/span>&lt;span class="nv">$NDK_HOME&lt;/span>&lt;span class="s2"> &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 编译的相关参数 &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 目标平台的CPU指令类型 ARMv8&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># armv7-a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">CPU&lt;/span>&lt;span class="o">=&lt;/span>armv8-a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 架构类型 ： ARM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ARCH&lt;/span>&lt;span class="o">=&lt;/span>arm64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 操作系统&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OS&lt;/span>&lt;span class="o">=&lt;/span>android
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 平台&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PLATFORM&lt;/span>&lt;span class="o">=&lt;/span>aarch64-linux-android
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OPTIMIZE_CFLAGS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;-march=&lt;/span>&lt;span class="nv">$CPU&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 指定输出路径&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PREFIX&lt;/span>&lt;span class="o">=&lt;/span>~/tools/outputs/ffmpeg/aarch64/&lt;span class="nv">$CPU&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># SYSROOT&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">SYSROOT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$TOOLCHAIN_PREFIX&lt;/span>/sysroot
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 交叉编译工具链&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">CROSS_PREFIX&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$TOOLCHAIN_PREFIX&lt;/span>/bin/llvm-
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Android交叉编译工具链的位置&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ANDROID_CROSS_PREFIX&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$TOOLCHAIN_PREFIX&lt;/span>/bin/&lt;span class="si">${&lt;/span>&lt;span class="nv">PLATFORM&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="m">29&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; FFMPEG 开始编译 &amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ffmpeg所有configure&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># https://github.com/FFmpeg/FFmpeg/blob/master/configure&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./configure &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --prefix&lt;span class="o">=&lt;/span>&lt;span class="nv">$PREFIX&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --enable-shared &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --enable-gpl &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --enable-neon &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --enable-hwaccels &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --enable-postproc &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --enable-jni &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --enable-small &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --enable-mediacodec &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --enable-decoder&lt;span class="o">=&lt;/span>h264_mediacodec &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --enable-ffmpeg &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-ffplay &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-ffprobe &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-ffplay &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-avdevice &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-debug &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-static &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-symver &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --cross-prefix&lt;span class="o">=&lt;/span>&lt;span class="nv">$CROSS_PREFIX&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --target-os&lt;span class="o">=&lt;/span>&lt;span class="nv">$OS&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --arch&lt;span class="o">=&lt;/span>&lt;span class="nv">$ARCH&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --cpu&lt;span class="o">=&lt;/span>&lt;span class="nv">$CPU&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --cc&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">ANDROID_CROSS_PREFIX&lt;/span>&lt;span class="si">}&lt;/span>-clang &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --cxx&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">ANDROID_CROSS_PREFIX&lt;/span>&lt;span class="si">}&lt;/span>-clang++ &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --enable-cross-compile &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --sysroot&lt;span class="o">=&lt;/span>&lt;span class="nv">$SYSROOT&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --extra-cflags&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;-Os -fPIC &lt;/span>&lt;span class="nv">$OPTIMIZE_CFLAGS&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --extra-ldflags&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$ADDI_LDFLAGS&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">make clean
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建目标路径,如果不存在的话，最终产物存储在Prefix对应路径之下。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkdir -p &lt;span class="nv">$PREFIX&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo make -j8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo make install
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; 编译完成，产物存储在:&lt;/span>&lt;span class="nv">$PREFIX&lt;/span>&lt;span class="s2"> &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>找不到从哪抄来的了, 找到了再补上&lt;/p>
&lt;/blockquote>
&lt;p>‍&lt;/p>
&lt;p>编译成功后产物的路径中生成四个文件夹, 分别是bin, include, lib, share.&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>文件夹&lt;/th>
 &lt;th>包含内容&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>bin&lt;/td>
 &lt;td>可执行文件, Android中不需要&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>lib&lt;/td>
 &lt;td>so文件&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>include&lt;/td>
 &lt;td>头文件&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>share&lt;/td>
 &lt;td>示例, 文档. 编译configure禁用了文档, 因此只有示例&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>‍&lt;/p>
&lt;h2 class="heading" id="集成">
 集成&lt;span class="heading__anchor"> &lt;a href="#%e9%9b%86%e6%88%90">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Android Studio中创建native module.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>将产物中的include文件夹复制到新建module的src/main/cpp文件夹下&lt;/p>
&lt;/li>
&lt;li>
&lt;p>产物lib文件夹下的so文件复制到module的src/main/jniLibs/ABI&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>‍&lt;/p>
&lt;p>ABI取决于你编译的. 比如你编译的是arm64-v8a的, 上面的ABI就是arm64-v8a. 那么你需要的目标路径就是src/main/jniLibs/arm64-v8a&lt;/p>
&lt;p>‍&lt;/p>
&lt;h3 class="heading" id="cmakeliststxt">
 CMakeLists.txt&lt;span class="heading__anchor"> &lt;a href="#cmakeliststxt">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># cmake version, 创建native module升成
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">cmake_minimum_required&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">VERSION&lt;/span> &lt;span class="s">3.22.1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 项目名, 创建native module升成
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;native_ffmpeg&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c">#引入头文件, 引入include文件夹
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">include_directories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">include&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_PROJECT_NAME&lt;/span>&lt;span class="o">}&lt;/span> &lt;span class="s">SHARED&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c"># List C/C++ source files with relative paths to this CMakeLists.txt.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span> &lt;span class="s">native_ffmpeg.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Set a normal, cache, or environment variable to a given value
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># cmake官方这么写解释set, 设置变量
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ANDROID_SO_DIR&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_SOURCE_DIR&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="s">/../jniLibs/&lt;/span>&lt;span class="o">${&lt;/span>&lt;span class="nv">ANDROID_ABI&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># add_library: Add a library to the project using the specified source files.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># set_target_properties: Sets properties on targets. The syntax for the command is to list all the targets you want to change, and then provide the values you want to set next.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># avcodec
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">avcodec&lt;/span> &lt;span class="s">SHARED&lt;/span> &lt;span class="s">IMPORTED&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set_target_properties&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">avcodec&lt;/span> &lt;span class="s">PROPERTIES&lt;/span> &lt;span class="s">IMPORTED_LOCATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">ANDROID_SO_DIR&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="s">/libavcodec.so&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># avfilter
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">avfilter&lt;/span> &lt;span class="s">SHARED&lt;/span> &lt;span class="s">IMPORTED&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set_target_properties&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">avfilter&lt;/span> &lt;span class="s">PROPERTIES&lt;/span> &lt;span class="s">IMPORTED_LOCATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">ANDROID_SO_DIR&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="s">/libavfilter.so&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># avformat
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">avformat&lt;/span> &lt;span class="s">SHARED&lt;/span> &lt;span class="s">IMPORTED&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set_target_properties&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">avformat&lt;/span> &lt;span class="s">PROPERTIES&lt;/span> &lt;span class="s">IMPORTED_LOCATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">ANDROID_SO_DIR&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="s">/libavformat.so&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># avutil
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">avutil&lt;/span> &lt;span class="s">SHARED&lt;/span> &lt;span class="s">IMPORTED&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set_target_properties&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">avutil&lt;/span> &lt;span class="s">PROPERTIES&lt;/span> &lt;span class="s">IMPORTED_LOCATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">ANDROID_SO_DIR&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="s">/libavutil.so&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># postproc
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">postproc&lt;/span> &lt;span class="s">SHARED&lt;/span> &lt;span class="s">IMPORTED&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set_target_properties&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">postproc&lt;/span> &lt;span class="s">PROPERTIES&lt;/span> &lt;span class="s">IMPORTED_LOCATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">ANDROID_SO_DIR&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="s">/libpostproc.so&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># swresample
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">swresample&lt;/span> &lt;span class="s">SHARED&lt;/span> &lt;span class="s">IMPORTED&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set_target_properties&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">swresample&lt;/span> &lt;span class="s">PROPERTIES&lt;/span> &lt;span class="s">IMPORTED_LOCATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">ANDROID_SO_DIR&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="s">/libswresample.so&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># swscale
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">swscale&lt;/span> &lt;span class="s">SHARED&lt;/span> &lt;span class="s">IMPORTED&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set_target_properties&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">swscale&lt;/span> &lt;span class="s">PROPERTIES&lt;/span> &lt;span class="s">IMPORTED_LOCATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">ANDROID_SO_DIR&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="s">/libswscale.so&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Specify libraries or flags to use when linking a given target and/or its dependents.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 还是cmake官方的解释
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_PROJECT_NAME&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c"># List libraries link to the target library
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span> &lt;span class="s">android&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">log&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c"># ffmpeg libs
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span> &lt;span class="s">avcodec&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">avfilter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">avformat&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">avutil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">postproc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">swresample&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">swscale&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>命令详情参考:&lt;/p>
&lt;p>&lt;a href="https://juejin.cn/post/6952049081464127518">Android NDK开发基础NDK即Native Development Kit，是Android上用来开发c/c++ - 掘金&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://cmake.org/cmake/help/latest/guide/tutorial/index.html">CMake Tutorial — CMake 3.30.3 Documentation&lt;/a>&lt;/p>
&lt;p>‍&lt;/p>
&lt;p>最后, 点击Android Studio上的小锤子开始build, 完成在module的build/outputs/aar中看到aar.&lt;/p>
&lt;p>‍&lt;/p>
&lt;h2 class="heading" id="调用">
 调用&lt;span class="heading__anchor"> &lt;a href="#%e8%b0%83%e7%94%a8">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>编辑JNI文件&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c++" data-lang="c++">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;jni.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;string&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// CPP中添加C的头文件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">extern&lt;/span> &lt;span class="s">&amp;#34;C&amp;#34;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;libavcodec/avcodec.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">extern&lt;/span> &lt;span class="s">&amp;#34;C&amp;#34;&lt;/span> &lt;span class="n">JNIEXPORT&lt;/span> &lt;span class="n">jstring&lt;/span> &lt;span class="n">JNICALL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Java_com_example_native_1ffmpeg_NativeLib_stringFromJNI&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">JNIEnv&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">jobject&lt;/span> &lt;span class="cm">/* this */&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">NewStringUTF&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">avcodec_configuration&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>懒人提示: 先写Java native方法或者Kotlin external方法由IDE提示生成CPP文件更方便&lt;/p>
&lt;/blockquote>
&lt;p>‍&lt;/p>
&lt;p>依赖ffmpeg模块&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gradle" data-lang="gradle">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="n">implementation&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s2">&amp;#34;:native-ffmpeg&amp;#34;&lt;/span>&lt;span class="o">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Activity:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-Kotlin" data-lang="Kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MainActivity&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">AppCompatActivity&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">onCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Bundle&lt;/span>&lt;span class="p">?)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">onCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">enableEdgeToEdge&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">setContentView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">layout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">activity_main&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">ViewCompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">setOnApplyWindowInsetsListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">findViewById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">insets&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">systemBars&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">insets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getInsets&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">WindowInsetsCompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">Type&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">systemBars&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">v&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">setPadding&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">systemBars&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">left&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">systemBars&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">top&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">systemBars&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">right&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">systemBars&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">bottom&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">insets&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="c1">// Return the libavcodec build-time configuration
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">findViewById&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">TextView&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="nc">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">tv_greeting&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">NativeLib&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">stringFromJNI&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>‍&lt;/p></description></item><item><title>Android Studio禁用富文本复制</title><link>https://opoojkk.com/posts/2024/08/06/android-studio%E7%A6%81%E7%94%A8%E5%AF%8C%E6%96%87%E6%9C%AC%E5%A4%8D%E5%88%B6/</link><pubDate>Tue, 06 Aug 2024 07:12:57 +0800</pubDate><guid>https://opoojkk.com/posts/2024/08/06/android-studio%E7%A6%81%E7%94%A8%E5%AF%8C%E6%96%87%E6%9C%AC%E5%A4%8D%E5%88%B6/</guid><description>&lt;p>富文本(rich text)和 纯文本(plain text)&lt;/p>
&lt;blockquote>
&lt;p>plain text format contains no formatting information, rich text format includes formatting details such as font size, style, color, and other elements like images, tables, and links.&lt;/p>
&lt;/blockquote>
&lt;p>Android Studio(Idea)中复制代码默认复制的是富文本(rich text),需要从 Android Studio 中复制出代码整理文档时不胜其烦.&lt;/p>
&lt;p>记录下关掉默认复制富文本的设置,帮助有同样困扰的朋友.&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/hSqNKom.png">&lt;/figure></description></item><item><title>面向过程和面向对象</title><link>https://opoojkk.com/posts/2024/08/04/%E9%9D%A2%E5%90%91%E8%BF%87%E7%A8%8B%E5%92%8C%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/</link><pubDate>Sun, 04 Aug 2024 16:50:33 +0800</pubDate><guid>https://opoojkk.com/posts/2024/08/04/%E9%9D%A2%E5%90%91%E8%BF%87%E7%A8%8B%E5%92%8C%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/</guid><description>&lt;p>小弟以为差别已经体现在了名字上，顾名思义，面向过程即关注点在过程上，通过组合不同的函数（方法）实现某种功能；而面向对象关注点在对象（实例）上，由一个或多个对象的方法，借助属性，共同实现。
至于其他的都是上面的差异更具体的表现，而不是原因。&lt;/p></description></item><item><title>TextView setText发生了什么？</title><link>https://opoojkk.com/posts/2024/07/28/textview-settext%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88/</link><pubDate>Sun, 28 Jul 2024 09:09:32 +0800</pubDate><guid>https://opoojkk.com/posts/2024/07/28/textview-settext%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88/</guid><description>&lt;h3 class="heading" id="前置知识">
 前置知识&lt;span class="heading__anchor"> &lt;a href="#%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%af%86">#&lt;/a>&lt;/span>
&lt;/h3>&lt;h3 class="heading" id="span">
 Span&lt;span class="heading__anchor"> &lt;a href="#span">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>既然是setText，想必你一定听过Span的大名，借助Span和SpannableString就可以让文字不光是文字，文字颜色、背景颜色、文字点击、链接还可以自定义更复杂的效果，&lt;/p>
&lt;h3 class="heading" id="movementmethod">
 MovementMethod&lt;span class="heading__anchor"> &lt;a href="#movementmethod">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>文字上的点击、光标选中都是由它实现的，详细的之后单独写。&lt;/p>
&lt;h3 class="heading" id="editor">
 Editor&lt;span class="heading__anchor"> &lt;a href="#editor">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>和编辑文字相关，之后也单独写。&lt;/p>
&lt;h4 class="heading" id="mtransformation">
 mTransformation&lt;span class="heading__anchor"> &lt;a href="#mtransformation">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Sets the transformation that is applied to the text that this
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * TextView is displaying.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @attr ref android.R.styleable#TextView_password
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @attr ref android.R.styleable#TextView_singleLine
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">setTransformationMethod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TransformationMethod&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">method&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mEditor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mEditor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setTransformationMethod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">method&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setTransformationMethodInternal&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">method&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 class="heading" id="buffertype">
 BufferType&lt;span class="heading__anchor"> &lt;a href="#buffertype">#&lt;/a>&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// definition&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@UnsupportedAppUsage&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BufferType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mBufferType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BufferType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">NORMAL&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Type of the text buffer that defines the characteristics of the text such as static,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * styleable, or editable.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">enum&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BufferType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">NORMAL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SPANNABLE&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">EDITABLE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="正文">
 正文&lt;span class="heading__anchor"> &lt;a href="#%e6%ad%a3%e6%96%87">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>作为Android中最基本的控件，TextView简直是无处不在，最最常用的用法像是&lt;code>textView.setText(&amp;quot;test&amp;quot;)&lt;/code>，无论是传入String，还是SpannableString都能显示，为什么能起作用？&lt;/p>
&lt;p>从&lt;code>setText&lt;/code>方法找到方法实现：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CharSequence&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mBufferType&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>嗯？CharSequence?&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">String&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Comparable&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span> &lt;span class="n">CharSequence&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">..&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">expect&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">StringBuilder&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Appendable&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CharSequence&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">..&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">SpannableString&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">extends&lt;/span> &lt;span class="n">SpannableStringInternal&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">implements&lt;/span> &lt;span class="n">CharSequence&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">GetChars&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Spannable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">..&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Android中String和StringBuilder都是CharSequence的子类。&lt;/p>
&lt;p>mBufferType又是什么东西？先不管它，接着往下找。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Sets the text to be displayed and the {@link android.widget.TextView.BufferType}.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * &amp;lt;p/&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * When required, TextView will use {@link android.text.Spannable.Factory} to create final or
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * intermediate {@link Spannable Spannables}. Likewise it will use
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@link android.text.Editable.Factory} to create final or intermediate
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@link Editable Editables}.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Subclasses overriding this method should ensure that the following post condition holds,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * in order to guarantee the safety of the view&amp;#39;s measurement and layout operations:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * regardless of the input, after calling #setText both {@code mText} and {@code mTransformed}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * will be different from {@code null}.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param text text to be displayed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param type a {@link android.widget.TextView.BufferType} which defines whether the text is
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * stored as a static text, styleable/spannable text, or editable text
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @see #setText(CharSequence)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @see android.widget.TextView.BufferType
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @see #setSpannableFactory(Spannable.Factory)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @see #setEditableFactory(Editable.Factory)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @attr ref android.R.styleable#TextView_text
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @attr ref android.R.styleable#TextView_bufferType
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CharSequence&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BufferType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// drop any potential mCharWrappper leaks&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mCharWrapper&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>又是嵌套，从上面的代码注释中知道了两点：&lt;/p>
&lt;ol>
&lt;li>text是传入的文字内容；&lt;/li>
&lt;li>type定义展示文字类型。&lt;/li>
&lt;/ol>
&lt;p>&lt;code>private void setText(CharSequence text, BufferType type,boolean notifyBefore, int oldlen)&lt;/code>
聪明的你从名字上一定看出了一些什么，notifyBefore大概率是要不要调用callback了，oldlen是长度。
在看代码实现前，你没有疑问吗？即使setText传入SpannableString，type我们可没改，也就是说用的是默认的，里面一定有判断text类型的逻辑。
去代码里找答案。（大量代码预警）&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;span class="lnt">124
&lt;/span>&lt;span class="lnt">125
&lt;/span>&lt;span class="lnt">126
&lt;/span>&lt;span class="lnt">127
&lt;/span>&lt;span class="lnt">128
&lt;/span>&lt;span class="lnt">129
&lt;/span>&lt;span class="lnt">130
&lt;/span>&lt;span class="lnt">131
&lt;/span>&lt;span class="lnt">132
&lt;/span>&lt;span class="lnt">133
&lt;/span>&lt;span class="lnt">134
&lt;/span>&lt;span class="lnt">135
&lt;/span>&lt;span class="lnt">136
&lt;/span>&lt;span class="lnt">137
&lt;/span>&lt;span class="lnt">138
&lt;/span>&lt;span class="lnt">139
&lt;/span>&lt;span class="lnt">140
&lt;/span>&lt;span class="lnt">141
&lt;/span>&lt;span class="lnt">142
&lt;/span>&lt;span class="lnt">143
&lt;/span>&lt;span class="lnt">144
&lt;/span>&lt;span class="lnt">145
&lt;/span>&lt;span class="lnt">146
&lt;/span>&lt;span class="lnt">147
&lt;/span>&lt;span class="lnt">148
&lt;/span>&lt;span class="lnt">149
&lt;/span>&lt;span class="lnt">150
&lt;/span>&lt;span class="lnt">151
&lt;/span>&lt;span class="lnt">152
&lt;/span>&lt;span class="lnt">153
&lt;/span>&lt;span class="lnt">154
&lt;/span>&lt;span class="lnt">155
&lt;/span>&lt;span class="lnt">156
&lt;/span>&lt;span class="lnt">157
&lt;/span>&lt;span class="lnt">158
&lt;/span>&lt;span class="lnt">159
&lt;/span>&lt;span class="lnt">160
&lt;/span>&lt;span class="lnt">161
&lt;/span>&lt;span class="lnt">162
&lt;/span>&lt;span class="lnt">163
&lt;/span>&lt;span class="lnt">164
&lt;/span>&lt;span class="lnt">165
&lt;/span>&lt;span class="lnt">166
&lt;/span>&lt;span class="lnt">167
&lt;/span>&lt;span class="lnt">168
&lt;/span>&lt;span class="lnt">169
&lt;/span>&lt;span class="lnt">170
&lt;/span>&lt;span class="lnt">171
&lt;/span>&lt;span class="lnt">172
&lt;/span>&lt;span class="lnt">173
&lt;/span>&lt;span class="lnt">174
&lt;/span>&lt;span class="lnt">175
&lt;/span>&lt;span class="lnt">176
&lt;/span>&lt;span class="lnt">177
&lt;/span>&lt;span class="lnt">178
&lt;/span>&lt;span class="lnt">179
&lt;/span>&lt;span class="lnt">180
&lt;/span>&lt;span class="lnt">181
&lt;/span>&lt;span class="lnt">182
&lt;/span>&lt;span class="lnt">183
&lt;/span>&lt;span class="lnt">184
&lt;/span>&lt;span class="lnt">185
&lt;/span>&lt;span class="lnt">186
&lt;/span>&lt;span class="lnt">187
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CharSequence&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BufferType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">notifyBefore&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">oldlen&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mTextSetFromXmlOrResourceId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 移除SuggestionSpan，类似Word中拼写检查&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// If suggestions are not enabled, remove the suggestion spans from the text&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">isSuggestionsEnabled&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">removeSuggestionSpans&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">mUserSetTextScaleX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mTextPaint&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setTextScaleX&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">0f&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 跑马灯效果&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">instanceof&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">getSpanStart&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TextUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">TruncateAt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MARQUEE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewConfiguration&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mContext&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">isFadingMarqueeEnabled&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setHorizontalFadingEdgeEnabled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mMarqueeFadeMode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MARQUEE_FADE_NORMAL&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setHorizontalFadingEdgeEnabled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mMarqueeFadeMode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setEllipsize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TextUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">TruncateAt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MARQUEE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 按照mFilters过滤文字&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mFilters&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">CharSequence&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">out&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mFilters&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">filter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">EMPTY_SPANNED&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">out&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">out&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 方法中第三个参数起作用&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">notifyBefore&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mText&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">oldlen&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mText&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sendBeforeTextChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mText&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">oldlen&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sendBeforeTextChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">needEditableForNotification&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mListeners&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mListeners&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">size&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">needEditableForNotification&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// TextView文字排版相关&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">PrecomputedText&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">precomputed&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">instanceof&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PrecomputedText&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PrecomputedText&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 可以编辑的内容重置状态，deep copy，最重要的是初始化Editor，后面用到。&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BufferType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">EDITABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getKeyListener&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">needEditableForNotification&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">createEditorIfNeeded&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mEditor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">forgetUndoRedo&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mEditor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">scheduleRestartInputForSetText&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Editable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mEditableFactory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">newEditable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setFilters&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mFilters&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">precomputed&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mTextDir&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mTextDir&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getTextDirectionHeuristic&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">autoPhraseBreaking&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">mUserSpeficiedLineBreakwordStyle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FeatureFlagUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isEnabled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mContext&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">FeatureFlagUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SETTINGS_AUTO_TEXT_WRAPPING&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nd">@PrecomputedText.Params.CheckResultUsableResult&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">checkResult&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">precomputed&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getParams&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">checkResultUsable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">getPaint&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mTextDir&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mBreakStrategy&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mHyphenationFrequency&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LineBreakConfig&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getLineBreakConfig&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mLineBreakStyle&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mLineBreakWordStyle&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">autoPhraseBreaking&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">switch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">checkResult&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PrecomputedText&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">UNUSABLE&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IllegalArgumentException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;PrecomputedText&amp;#39;s Parameters don&amp;#39;t match the parameters of this TextView.&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Consider using setTextMetricsParams(precomputedText.getParams()) &amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;to override the settings of this TextView: &amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;PrecomputedText: &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">precomputed&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getParams&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;TextView: &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getTextMetricsParams&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PrecomputedText&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">NEED_RECOMPUTE&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">precomputed&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PrecomputedText&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">precomputed&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getTextMetricsParams&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PrecomputedText&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">USABLE&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// pass through&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BufferType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPANNABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mMovement&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// SPANNABLE deep copy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mSpannableFactory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">newSpannable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">instanceof&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CharWrapper&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TextUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">stringOrSpannedString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@AccessibilityUtils.A11yTextChangeType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a11yTextChangeType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AccessibilityUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">NONE&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">AccessibilityManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInstance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mContext&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">isEnabled&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">a11yTextChangeType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AccessibilityUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">textOrSpanChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mText&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 设置autolink更新type&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mAutoLinkMask&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Spannable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s2&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BufferType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">EDITABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">instanceof&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spannable&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">s2&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Spannable&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">s2&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mSpannableFactory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">newSpannable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Linkify&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addLinks&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mAutoLinkMask&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s2&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BufferType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">EDITABLE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BufferType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">EDITABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BufferType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPANNABLE&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * We must go ahead and set the text before changing the
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * movement method, because setMovementMethod() may call
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * setText() again to try to upgrade the buffer type.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setTextInternal&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a11yTextChangeType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AccessibilityUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">NONE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">a11yTextChangeType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AccessibilityUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">PARCELABLE_SPAN&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Do not change the movement method for text that support text selection as it&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// would prevent an arbitrary cursor displacement.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mLinksClickable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">textCanBeSelected&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setMovementMethod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinkMovementMethod&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInstance&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 更新BufferType&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mBufferType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 更新文字&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setTextInternal&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 像是把密码替换成*的文字处理类&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mTransformation&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mTransformed&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mTransformed&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mTransformation&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getTransformation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mTransformed&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Should not happen if the transformation method follows the non-null postcondition.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mTransformed&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">textLength&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">isOffsetMapping&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mTransformed&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">instanceof&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OffsetMapping&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// !!!更新Span&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">instanceof&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spannable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">mAllowTransformationLengthChange&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">isOffsetMapping&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Spannable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Spannable&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Remove any ChangeWatchers that might have come from other TextViews.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ChangeWatcher&lt;/span>&lt;span class="o">[]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">watchers&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getSpans&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ChangeWatcher&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">watchers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">removeSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">watchers&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mChangeWatcher&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mChangeWatcher&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ChangeWatcher&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mChangeWatcher&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">textLength&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPAN_INCLUSIVE_INCLUSIVE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CHANGE_WATCHER_PRIORITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPAN_PRIORITY_SHIFT&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mEditor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mEditor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addSpanWatchers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mTransformation&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">priority&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">isOffsetMapping&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OFFSET_MAPPING_SPAN_PRIORITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mTransformation&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">textLength&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPAN_INCLUSIVE_INCLUSIVE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">priority&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPAN_PRIORITY_SHIFT&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mMovement&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mMovement&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">initialize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Spannable&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Initializing the movement method will have set the
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * selection, so reset mSelectionMoved to keep that from
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * interfering with the normal on-focus selection-setting.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mEditor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mEditor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mSelectionMoved&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mLayout&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">checkForRelayout&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sendOnTextChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">oldlen&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">textLength&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">onTextChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">oldlen&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">textLength&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mHideHint&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a11yTextChangeType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AccessibilityUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">TEXT&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">notifyViewAccessibilityStateChangedIfNeeded&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">AccessibilityEvent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">CONTENT_CHANGE_TYPE_TEXT&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a11yTextChangeType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AccessibilityUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">PARCELABLE_SPAN&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">notifyViewAccessibilityStateChangedIfNeeded&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">AccessibilityEvent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">CONTENT_CHANGE_TYPE_UNDEFINED&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">needEditableForNotification&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sendAfterTextChanged&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">Editable&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">notifyListeningManagersAfterTextChanged&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mEditor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// SelectionModifierCursorController depends on textCanBeSelected, which depends on text&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mEditor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">prepareCursorControllers&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mEditor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">maybeFireScheduledRestartInputForSetText&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>乍一看setTextInternal像是更新文字的，点进去才发现是更新变量的。
又一看&lt;code>mMovement.initialize(this, (Spannable) text)&lt;/code>也很像，点进去发现又是重置状态的。
大失所望，又一想文字改变的callback还没看呢！
sendOnTextChanged还是onTextChanged呢，一看sendOnTextChanged有内容而onTextChanged是空实现，那就是sendOnTextChanged。很遗憾虽然有callback，但也不是。&lt;/p>
&lt;h3 class="heading" id="从span入手">
 从Span入手&lt;span class="heading__anchor"> &lt;a href="#%e4%bb%8espan%e5%85%a5%e6%89%8b">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>ForegroundColorSpan起作用一定要调用什么方法，ForegroundColorSpan和BackgroundColorSpan同时是抽象类CharacterStyle的子类，updateDrawState方法是需要重写的，依我看十有八九是它。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mMetricAffectingSpanSpanSet&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">numberOfSpans&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// empty by construction. This special case in getSpans() explains the &amp;gt;= &amp;amp; &amp;lt;= tests&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">mMetricAffectingSpanSpanSet&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">spanStarts&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mStart&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mlimit&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mMetricAffectingSpanSpanSet&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">spanEnds&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mStart&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">continu&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">insideEllipsis&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mStart&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mEllipsisStart&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mMetricAffectingSpanSpanSet&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">spanStarts&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mMetricAffectingSpanSpanSet&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">spanEnds&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mStart&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mEllipsisEnd&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MetricAffectingSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">span&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mMetricAffectingSpanSpanSet&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">spans&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">span&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">instanceof&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ReplacementSpan&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">replacement&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">insideEllipsis&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ReplacementSpan&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">span&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// We might have a replacement that uses the draw&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// state, otherwise measure state would suffice.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">span&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">updateDrawState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">wp&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>验证了上面猜测。但是，Span可以点击呢，当然没有忘记在LinkMovementMethod中实现。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CLICK&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">selStart&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">selEnd&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ClickableSpan&lt;/span>&lt;span class="o">[]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">links&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getSpans&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">selStart&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">selEnd&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ClickableSpan&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nf">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">links&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ClickableSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">link&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">links&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">link&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">instanceof&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TextLinkSpan&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">TextLinkSpan&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">link&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">onClick&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">widget&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TextLinkSpan&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">INVOCATION_METHOD_KEYBOARD&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">link&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onClick&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">widget&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>WeakReference解决内存泄漏</title><link>https://opoojkk.com/posts/2024/06/28/weakreference%E8%A7%A3%E5%86%B3%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/</link><pubDate>Fri, 28 Jun 2024 21:37:09 +0800</pubDate><guid>https://opoojkk.com/posts/2024/06/28/weakreference%E8%A7%A3%E5%86%B3%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/</guid><description>&lt;p>内存泄漏是经常会遇到的问题，内存泄漏简单说就是已经分配出去的内存，不再使用时没有被正确释放，不能再次分配。代码中内存泄漏问题本质就是长生命周期的对象持有了短生命周期的对象，短生命周期对象不能被销毁。内存泄漏严重时会导致OOM。&lt;/p>
&lt;p>Java中的有四种内存，常用的分别是强引用、软引用和弱引用（Kotlin也是）。
强引用不用多说，只要是通过new关键字创建的对象都是，无论什么时候JVM都不会回收强引用，宁可触发OOM。&lt;/p>
&lt;p>被SoftReference包住的称为软引用，像是SoftReference&amp;lt;new Object()&amp;gt;的形式，内存不够时会回收掉&lt;em>&lt;strong>只有&lt;/strong>&lt;/em>软引用的对象(一个对象只被软引用)。&lt;/p>
&lt;p>弱引用和软引用的形式类似，弱引用的软件词：WeakReference，GC时发现&lt;em>&lt;strong>只有&lt;/strong>&lt;/em>弱引用的对象(一个对象只被弱引用)会立马回收。&lt;/p>
&lt;p>上面的描述和其他博客的看起来没有太大的差别，除了加粗的”只有“，关键页正是”只有“。&lt;/p>
&lt;p>举例：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">DemoActivity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AppCompatActivity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">protected&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">onCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@Nullable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInstance&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">setsSomethingListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SomethingListener&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">doSomething&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// do something&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">SomethingManager&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingManager&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingManager&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingManager&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getInstance&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sSomethingManager&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingManager&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingManager&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingManager&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingListener&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingListener&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">setsSomethingListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SomethingListener&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingListener&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">sSomethingListener&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingListener&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">run&lt;/span>&lt;span class="p">(){&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingListener&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">doSomething&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">interface&lt;/span> &lt;span class="nc">SomethingListener&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">doSomething&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>写一个最简单的例子，一个单例对象持有一个listener，理想的情况当然是Activity销毁的时候移除listener。总会有不理想的人忘记了，当Activity销毁时就发生了内容泄漏：长生命周期的对象引用了段生命周期的对象。用弱引用解决：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">DemoActivity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AppCompatActivity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SomethingListener&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mSomethingListener&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SomethingListener&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">doSomething&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// do something&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">protected&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">onCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@Nullable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInstance&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">setsSomethingListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">WeakReference&lt;/span>&lt;span class="o">&amp;lt;&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mSomethingListener&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">SomethingManager&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingManager&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingManager&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingManager&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getInstance&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sSomethingManager&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingManager&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingManager&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingManager&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">WeakReference&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">SomethingListener&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingListener&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">setsSomethingListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">WeakReference&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">SomethingListener&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingListener&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">sSomethingListener&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingListener&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">run&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">SomethingListener&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">somethingListener&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSomethingListener&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">somethingListener&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">somethingListener&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">doSomething&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">interface&lt;/span> &lt;span class="nc">SomethingListener&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">doSomething&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>与刚才的代码有两处不同：&lt;/p>
&lt;ol>
&lt;li>DemoActivity中新增了SomethingListener类型的成员变量；&lt;/li>
&lt;li>以弱引用的形式传入SomethingListener实例；&lt;/li>
&lt;/ol>
&lt;p>只持有弱引用的对象被GC发现会被立即回收，为了防止被回收，增加一个强引用类型的成员变量，当DemoActivity示例被回收后，强引用对象不存在了，这时候SomethingListener对象只在SomethingManager中有一个弱引用对象，GC时就会回收，因此解决了内存泄漏问题。&lt;/p></description></item><item><title>20240527-20240531</title><link>https://opoojkk.com/posts/2024/06/01/20240527-20240531/</link><pubDate>Sat, 01 Jun 2024 09:53:50 +0800</pubDate><guid>https://opoojkk.com/posts/2024/06/01/20240527-20240531/</guid><description>&lt;p>这一周累死累活，熬到了周六，期间发现了自身存在的一些问题：&lt;/p>
&lt;h2 class="heading" id="coding">
 Coding&lt;span class="heading__anchor"> &lt;a href="#coding">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>任务不清晰&lt;/p>
&lt;p>一周都在缝缝补补，临近ddl还是不知道还有些完成了，哪些没完成&lt;/p>
&lt;p>原因有几个：&lt;/p>
&lt;ul>
&lt;li>事前没拆分&lt;/li>
&lt;li>事中没记录&lt;/li>
&lt;li>事后没总结&lt;/li>
&lt;/ul>
&lt;p>措施：&lt;/p>
&lt;ul>
&lt;li>事前拆分任务，列表格&lt;/li>
&lt;li>过程中更新表格，做记录同时追踪进度&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>总是把任务想的简单&lt;/p>
&lt;p>原因：&lt;/p>
&lt;ul>
&lt;li>一部分原因是因为第一条，也有一部分是因为没有认真思考，着急给他人回复&lt;/li>
&lt;/ul>
&lt;p>措施：&lt;/p>
&lt;ul>
&lt;li>不要着急，着急解决不了问题，还容易出更多问题&lt;/li>
&lt;li>不能当场给出的，晚点给&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="为人处世">
 为人处世&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%ba%e4%ba%ba%e5%a4%84%e4%b8%96">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>谦逊&lt;/p>
&lt;p>厉害的同事不仅是写代码厉害，与人沟通也是一样，有人寻求帮助时，用两种不同的态度回应，最后的结果可能是一样的，都需要我完成某些任务，对其他人的感受是不同的&lt;/p>
&lt;/li>
&lt;li>
&lt;p>抛出问题&lt;/p>
&lt;p>过程中，难免遇到问题，不一定是因为我的能力不足导致的，可能是对接的同事或者技术问题导致。及时对齐，自己不能解决的问题push montor或者leader及时解决&lt;/p>
&lt;/li>
&lt;li>
&lt;p>不要畏惧与人沟通&lt;/p>
&lt;p>害怕和人沟通只会拖延，最后还是比避免不了，就算说错了也没关系&lt;/p>
&lt;/li>
&lt;li>
&lt;p>描述背景&lt;/p>
&lt;p>先让人了解发生的背景，清晰对方需要做什么，神之我提出的解决方案不一定是最好的，有助于更好的方案解决问题。&lt;/p>
&lt;/li>
&lt;/ol></description></item><item><title>gravity和layout_gravity</title><link>https://opoojkk.com/posts/2024/04/27/gravity%E5%92%8Clayout_gravity/</link><pubDate>Sat, 27 Apr 2024 19:52:30 +0800</pubDate><guid>https://opoojkk.com/posts/2024/04/27/gravity%E5%92%8Clayout_gravity/</guid><description>&lt;h1 class="heading" id="背景">
 背景&lt;span class="heading__anchor"> &lt;a href="#%e8%83%8c%e6%99%af">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>从开始写Android不断地有人告诉我，用gravity和layout_gravity控制View的位置，总是不明白两者有什么差别（中途明白过，又忘了😭），又是怎么实现的，最近想起来，赶紧查一查。&lt;/p>
&lt;h1 class="heading" id="实现">
 实现&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%9e%e7%8e%b0">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>首先gravity和layout_gravity都是View的一个属性，一直以为（没错，又是以为）类似height和width类似的，每个View都会有的，其实细想一下不是每个View都有。&lt;/p>
&lt;h2 class="heading" id="gravity">
 gravity&lt;span class="heading__anchor"> &lt;a href="#gravity">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>TextView是有gravity的：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 解析属性&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">internal&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">styleable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">TextView_gravity&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setGravity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attr&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// 赋值&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">setGravity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mGravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// mGravity定义及默认值&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mGravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">TOP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">START&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// View中能起作用一定是跟测量有关，找到相关代码&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getExtendedPaddingTop&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mGravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">VERTICAL_GRAVITY_MASK&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">TOP&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">top&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">BOTTOM&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">top&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">viewht&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">layoutht&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// (gravity == Gravity.CENTER_VERTICAL)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">top&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">viewht&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">layoutht&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">2&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="layout_gravity">
 layout_gravity&lt;span class="heading__anchor"> &lt;a href="#layout_gravity">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>layout_gravity哪有呢，LinearLayout：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">internal&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">styleable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayout_Layout_layout_gravity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>LinearLayout中布局子View的代码。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">layoutVertical&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">left&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">top&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">right&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">bottom&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">gravity&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">minorGravity&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">layoutDirection&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getLayoutDirection&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">absoluteGravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getAbsoluteGravity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">layoutDirection&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">switch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">absoluteGravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">HORIZONTAL_GRAVITY_MASK&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">CENTER_HORIZONTAL&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">childLeft&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">paddingLeft&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">childSpace&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childWidth&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">2&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">leftMargin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">rightMargin&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">RIGHT&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">childLeft&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childRight&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childWidth&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">rightMargin&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LEFT&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">default&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">childLeft&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">paddingLeft&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">leftMargin&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>恰好LinearLayout中也有用到gravity&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 取到gravity&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">internal&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">styleable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayout_gravity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setGravity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在设置baselineAligned为true时，gravity才会发挥作用。&lt;/p>
&lt;h2 class="heading" id="gravity-1">
 Gravity&lt;span class="heading__anchor"> &lt;a href="#gravity-1">#&lt;/a>&lt;/span>
&lt;/h2>&lt;blockquote>
&lt;p>Standard constants and tools for placing an object within a potentially larger container.
上面是源码中官方的注释。&lt;/p>
&lt;/blockquote>
&lt;p>可以发现代码中设置Gravity的地方用到了Gravity类，比较好理解Gravity类就是官方提供的一个工具类，让Gravity中每个属性代表的值统一。&lt;/p>
&lt;h1 class="heading" id="总结">
 总结&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h1>&lt;ol>
&lt;li>gravity和layout_gravity和那个View没有关系，你想自定义View的时候实现也可以用；&lt;/li>
&lt;li>gravity是控制View内部的内容从哪里开始放置，一般用在View上；&lt;/li>
&lt;li>layout_gravity是控制子View怎么布局，一般用在ViewGroup上；&lt;/li>
&lt;li>只要你想怎么实现都行。&lt;/li>
&lt;/ol></description></item><item><title>破解Android绘制常见误区</title><link>https://opoojkk.com/posts/2024/04/27/%E7%A0%B4%E8%A7%A3android%E7%BB%98%E5%88%B6%E5%B8%B8%E8%A7%81%E8%AF%AF%E5%8C%BA/</link><pubDate>Sat, 27 Apr 2024 16:56:51 +0800</pubDate><guid>https://opoojkk.com/posts/2024/04/27/%E7%A0%B4%E8%A7%A3android%E7%BB%98%E5%88%B6%E5%B8%B8%E8%A7%81%E8%AF%AF%E5%8C%BA/</guid><description>&lt;h1 class="heading" id="背景">
 背景&lt;span class="heading__anchor"> &lt;a href="#%e8%83%8c%e6%99%af">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>需求涉及到创建View，要求能在指定位置创建View。&lt;/p>
&lt;p>在此之前的理解是需要等到View attach到window上才能获取到View的宽高。按照这种思路，先创建一个View，添加到父View，再调整View的位置，尝试后效果不是很理想，会有View位置的变化，视觉上看有闪烁的效果。&lt;/p>
&lt;p>尝试通过ChatGPT解决，有了以下对话：&lt;/p>
&lt;p>&lt;a href="https://chat.openai.com/share/cf1c61d7-ccda-4611-bb46-937e044788d2">与ChatGPT的问话&lt;/a>&lt;/p>
&lt;p>View的绘制是可以在外部调用的，方法声明public。
所以，View设置内容后可以调用一遍measure方法以确定大小。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">fixedPosition&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">View&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">layoutParams&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nc">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">LayoutParams&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">LayoutParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WRAP_CONTENT&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">LayoutParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WRAP_CONTENT&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">measure&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">MeasureSpec&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">UNSPECIFIED&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">MeasureSpec&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">UNSPECIFIED&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">viewWidth&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">measuredWidth&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">viewHeight&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">measuredHeight&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">layoutParams&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nc">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">LayoutParams&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">LayoutParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WRAP_CONTENT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 宽度
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nc">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">LayoutParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WRAP_CONTENT&lt;/span> &lt;span class="c1">// 高度
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">).&lt;/span>&lt;span class="n">apply&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">marginStart&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="p">-&lt;/span> &lt;span class="n">viewWidth&lt;/span> &lt;span class="p">/&lt;/span> &lt;span class="m">2&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">toInt&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="c1">// 左边界坐标
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">topMargin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="p">-&lt;/span> &lt;span class="n">viewHeight&lt;/span> &lt;span class="p">/&lt;/span> &lt;span class="m">2&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">toInt&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="c1">// 上边界坐标
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">layoutParams&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">layoutParams&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>事后反思是因为先入为主的观念，认为既然View的绘制发生在View内部，就不能允许在外部调用。至于是不是这样，没有深入研究，值得注意。&lt;/p></description></item><item><title>Android资源匹配规则详解</title><link>https://opoojkk.com/posts/2024/04/27/android%E8%B5%84%E6%BA%90%E5%8C%B9%E9%85%8D%E8%A7%84%E5%88%99%E8%AF%A6%E8%A7%A3/</link><pubDate>Sat, 27 Apr 2024 16:48:29 +0800</pubDate><guid>https://opoojkk.com/posts/2024/04/27/android%E8%B5%84%E6%BA%90%E5%8C%B9%E9%85%8D%E8%A7%84%E5%88%99%E8%AF%A6%E8%A7%A3/</guid><description>&lt;p>总是不明白Android运行时使用哪个资源，资源匹配的规则是什么样的，找到对应文档，以此记录。
下方内容copy自&lt;a href="https://developer.android.google.cn/guide/topics/resources/providing-resources?hl=zh-cn#AlternativeResources">官方文档&lt;/a>，可直接访问查看。&lt;/p>
&lt;p>表 2 按优先级顺序列出了有效的配置限定符您可以通过使用短划线分隔每个限定符，从而向一个目录名称添加多个限定符。如果对资源目录使用多个限定符，则必须按照表中所列顺序将相应限定符添加到目录名称中。&lt;/p>
&lt;p>&lt;strong>表 2.&lt;/strong> 配置限定符名称。&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>配置&lt;/th>
 &lt;th>限定符值&lt;/th>
 &lt;th>说明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>MCC 和 MNC&lt;/td>
 &lt;td>示例： &lt;br>&lt;code>mcc310&lt;/code> &lt;br>&lt;code>mcc310-mnc004&lt;/code> &lt;br>&lt;code>mcc208-mnc00&lt;/code>&lt;/td>
 &lt;td>移动设备国家/地区代码 (MCC)，（可选）后跟设备 SIM 卡中的移动网络代码 (MNC)。例如，&lt;code>mcc310&lt;/code> 是指美国的任一运营商，&lt;code>mcc310-mnc004&lt;/code> 是指美国的 Verizon 公司，而 &lt;code>mcc208-mnc00&lt;/code> 是指法国的 Orange 公司。&lt;br>&lt;br>如果设备使用无线电连接（即为 GSM 手机），则 MCC 和 MNC 值来自 SIM 卡。&lt;br>&lt;br>您也可以单独使用 MCC，例如，将国家/地区特定的合法资源加入应用。如果只需根据语言指定，则改用语言、脚本（可选）和区域（可选）限定符（稍后进行介绍）。如果使用 MCC 和 MNC 限定符，请谨慎执行此操作并测试限定符是否按预期工作。&lt;br>&lt;br>另请参阅配置字段 &lt;code>[mcc](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#mcc)&lt;/code> 和 &lt;code>[mnc](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#mnc)&lt;/code>，二者分别表示当前的移动设备国家/地区代码和移动网络代码。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>语言、脚本（可选）和区域（可选）&lt;/td>
 &lt;td>示例： &lt;br>&lt;code>en&lt;/code> &lt;br>&lt;code>fr&lt;/code> &lt;br>&lt;code>en-rUS&lt;/code> &lt;br>&lt;code>fr-rFR&lt;/code> &lt;br>&lt;code>fr-rCA&lt;/code> &lt;br>&lt;code>b+en&lt;/code> &lt;br>&lt;code>b+en+US&lt;/code> &lt;br>&lt;code>b+es+419&lt;/code> &lt;br>&lt;code>b+zh+Hant&lt;/code> &lt;br>&lt;code>b+sr+Latn+RS&lt;/code>&lt;/td>
 &lt;td>语言通过由两个字母组成的 &lt;a href="http://www.loc.gov/standards/iso639-2/php/code_list.php">ISO 639-1&lt;/a> 语言代码进行定义，可以选择后跟两个字母组成的 &lt;a href="https://www.iso.org/obp/ui/#iso:pub:PUB500001:en">ISO 3166-1-alpha-2&lt;/a> 区域码（前缀用小写字母 &lt;code>r&lt;/code>）。&lt;br>&lt;br>这些代码不区分大小写。&lt;code>r&lt;/code> 前缀用于区分区域码。您不能单独指定区域。&lt;br>&lt;br>Android 7.0（API 级别 24）引入对 &lt;a href="https://tools.ietf.org/html/bcp47">BCP 47 语言标记&lt;/a>的支持，可供您用来限定特定于语言和区域的资源。语言标记由一个或多个子标记序列组成，每个子标记都能优化或缩小由整体标记标识的语言范围。如需了解有关语言标记的详细信息，请参阅&lt;a href="https://tools.ietf.org/html/rfc5646">用于标识语言的标记&lt;/a>。&lt;br>&lt;br>如要使用 BCP 47 语言标记，请将 &lt;code>b+&lt;/code> 和两个字母的 &lt;a href="http://www.loc.gov/standards/iso639-2/php/code_list.php">ISO 639-1&lt;/a> 语言代码串联；其后还可选择使用其他子标记，用 &lt;code>+&lt;/code> 分隔即可。&lt;br>&lt;br>如果用户在系统设置中更改语言，则语言标记可能会在应用的生命周期中发生变更。如需了解这会在运行时期间给应用带来哪些影响，请参阅&lt;a href="https://developer.android.google.cn/guide/topics/resources/runtime-changes?hl=zh-cn">处理配置更改&lt;/a>。&lt;br>&lt;br>如需查看将应用本地化为其他语言的完整指南，请参阅&lt;a href="https://developer.android.google.cn/guide/topics/resources/localization?hl=zh-cn">本地化您的应用&lt;/a>。&lt;br>&lt;br>另请参阅 &lt;code>[getLocales()](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#getLocales())&lt;/code> 方法，了解该方法提供的已定义语言区域列表。此列表包含主要的语言区域。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>布局方向&lt;/td>
 &lt;td>&lt;code>ldrtl&lt;/code> &lt;br>&lt;code>ldltr&lt;/code>&lt;/td>
 &lt;td>应用的布局方向。&lt;code>ldrtl&lt;/code> 是指“布局方向从右到左”。&lt;code>ldltr&lt;/code> 是指“布局方向从左到右”（默认的隐式值）。&lt;br>&lt;br>此配置适用于布局、可绘制对象或值等任何资源。&lt;br>&lt;br>例如，如需针对阿拉伯语提供某种特定布局，并针对任何其他“从右到左”的语言（例如波斯语或希伯来语）提供某种通用布局，则可使用以下目录：&lt;br>&lt;br>&lt;code>res/&lt;/code> &lt;br>  &lt;code>layout/&lt;/code> &lt;br>    &lt;code>main.xml&lt;/code>（默认布局） &lt;br>  &lt;code>layout-ar/&lt;/code> &lt;br>    &lt;code>main.xml&lt;/code>（阿拉伯语的特定布局） &lt;br>  &lt;code>layout-ldrtl/&lt;/code> &lt;br>    &lt;code>main.xml&lt;/code>（阿拉伯语以外的任何从右到左的语言，因为“ar”语言限定符的优先级较高）&lt;br>&lt;br>&lt;strong>注意&lt;/strong>：如要为应用启用从右到左的布局功能，则必须将 &lt;a href="https://developer.android.google.cn/guide/topics/manifest/application-element?hl=zh-cn#supportsrtl">&lt;code>SupportsRtl&lt;/code>&lt;/a> 设置为 &lt;code>&amp;quot;true&amp;quot;&lt;/code>，并将 &lt;a href="https://developer.android.google.cn/guide/topics/manifest/uses-sdk-element?hl=zh-cn#target">&lt;code>TargetSdkVersion&lt;/code>&lt;/a> 设置为 17 或更高版本。&lt;br>&lt;br>此项为 API 级别 17 中的新增配置。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>最小宽度&lt;/td>
 &lt;td>&lt;code>sw&amp;lt;N&amp;gt;dp&lt;/code> &lt;br> &lt;br>示例： &lt;br>&lt;code>sw320dp&lt;/code> &lt;br>&lt;code>sw600dp&lt;/code> &lt;br>&lt;code>sw720dp&lt;/code> &lt;br>等等&lt;/td>
 &lt;td>应用可用屏幕区域的最短尺寸。具体而言，应用窗口的 &lt;code>smallestWidth&lt;/code> 是窗口可用高度和宽度的最小尺寸。您也可将其视为窗口的“最小可能宽度”。您可使用此限定符，让应用界面的可用宽度至少为 &lt;code>&amp;lt;N&amp;gt;&lt;/code> dp。&lt;br>&lt;br>例如，如果布局要求屏幕区域的最小尺寸始终至少为 600 dp，则可使用此限定符在 &lt;code>res/layout-sw600dp/&lt;/code> 目录中创建布局资源。仅当可用屏幕的最小尺寸至少为 600 dp（无论 600 dp 表示的边是用户所认为的高度还是宽度）时，系统才会使用这些资源。调整窗口大小（更改可用宽度/高度）或重新定位（可能会更改系统边衬区）时，最小宽度可能会改变。&lt;br>&lt;br>使用最小宽度确定一般屏幕尺寸非常有用，因为宽度通常是设计布局时的驱动因素。界面通常是竖直滚动的，但其在水平方向上所需的最小空间则相对有很多的限制。&lt;br>&lt;br>可用宽度也是确定是否针对手机使用单窗格布局，或针对平板电脑使用多窗格布局的关键因素。因此，您可能最关注每台设备上的最小可能宽度。&lt;br>&lt;br>设备的最小宽度会将屏幕装饰元素和系统界面考虑在内。例如，如果设备屏幕上的永久性界面元素沿着最小宽度轴占据空间，则系统会声明最小宽度小于实际屏幕尺寸，因为这些屏幕像素不适用于您的界面。&lt;br>&lt;br>以下是一些可用于常见屏幕尺寸的值：&lt;br>&lt;br>- 320，适用于屏幕配置如下的设备：&lt;br> - 240x320 ldpi（QVGA 手机）&lt;br> - 320x480 mdpi（手机）&lt;br> - 480x800 hdpi（高密度手机）&lt;br>- 480，适用于 480x800 mdpi 之类的屏幕（平板电脑/手机）&lt;br>- 600，适用于 600x1024 mdpi 之类的屏幕（7 英寸平板电脑）&lt;br>- 720，适用于 720x1280 mdpi 之类的屏幕（10 英寸平板电脑）&lt;br>&lt;br>当应用为多个资源目录提供不同的 &lt;code>smallestWidth&lt;/code> 限定符值时，系统会使用最接近（但未超出）设备 &lt;code>smallestWidth&lt;/code> 的值。&lt;br>&lt;br>&lt;em>此项为 API 级别 13 中的新增配置。&lt;/em>&lt;br>&lt;br>另请参阅 &lt;a href="https://developer.android.google.cn/guide/topics/manifest/supports-screens-element?hl=zh-cn#requiresSmallest">&lt;code>android:requiresSmallestWidthDp&lt;/code>&lt;/a> 属性（声明与应用兼容的最小 &lt;code>smallestWidth&lt;/code>）和 &lt;code>[smallestScreenWidthDp](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#smallestScreenWidthDp)&lt;/code> 配置字段（存放设备的 &lt;code>smallestWidth&lt;/code> 值）。&lt;br>&lt;br>如需详细了解如何使用此限定符针对不同的屏幕进行设计，请参阅&lt;a href="https://developer.android.google.cn/guide/topics/large-screens/support-different-screen-sizes?hl=zh-cn">支持不同的屏幕尺寸&lt;/a>。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>可用宽度和高度&lt;/td>
 &lt;td>&lt;code>w&amp;lt;N&amp;gt;dp&lt;/code> &lt;br>&lt;code>h&amp;lt;N&amp;gt;dp&lt;/code> &lt;br> &lt;br>示例： &lt;br>&lt;code>w720dp&lt;/code> &lt;br>&lt;code>w1024dp&lt;/code> &lt;br>&lt;code>h720dp&lt;/code> &lt;br>&lt;code>h1024dp&lt;/code> &lt;br>等等&lt;/td>
 &lt;td>指定资源使用的最小可用屏幕宽度或高度（以 &lt;code>dp&lt;/code> 为单位，由 &lt;code>&amp;lt;N&amp;gt;&lt;/code> 值定义）。当设备屏幕方向在横屏和竖屏之间切换时、设备折叠或展开时或者系统进入或退出多窗口模式时，系统会将这些配置值与当前的显示宽度和高度进行比较。在多窗口模式下，这些值反映的是包含应用的窗口的宽度和高度，而不是设备屏幕的宽度和高度。同样，对于嵌入的 activity，该值与各个 activity 的宽度和高度（而非屏幕的宽度和高度）。如需了解详情，请参阅 &lt;a href="https://developer.android.google.cn/guide/topics/large-screens/activity-embedding?hl=zh-cn">activity 嵌入&lt;/a>。&lt;br>&lt;br>可用宽度和高度通常有助于确定是否使用多窗格布局，因为即使是在平板电脑设备上，您通常也不希望在竖屏模式下像横屏模式那样使用多窗格布局。因此，您可以使用这些限定符来指定布局所需的最小宽度和/或高度，而无需同时使用屏幕尺寸和屏幕方向限定符。&lt;br>&lt;br>当应用为这些配置提供具有不同值的多个资源目录时，系统会使用最接近（但未超出）设备当前屏幕宽度的值。系统将实际屏幕宽度与指定宽度之间的差值和实际屏幕高度与指定高度之间的差值相加（未指定的高度和宽度值为 0），以确定最接近的目录。&lt;br>&lt;br>该值会排除&lt;a href="https://developer.android.google.cn/reference/android/view/WindowInsets?hl=zh-cn">窗口边衬区&lt;/a>占据的区域，因此，如果设备显示屏的边缘具有永久性界面元素，则宽度和高度值会小于实际屏幕尺寸（即使应用使用 &lt;a href="https://developer.android.google.cn/reference/android/view/Window?hl=zh-cn#setDecorFitsSystemWindows(boolean)">&lt;code>Window.setDecorFitsSystemWindows&lt;/code>&lt;/a> 或 &lt;a href="https://developer.android.google.cn/reference/androidx/core/view/WindowCompat?hl=zh-cn#setDecorFitsSystemWindows(android.view.Window,boolean)">&lt;code>WindowCompat.setDecorFitsSystemWindows&lt;/code>&lt;/a> 以边缘对齐的方式显示）。&lt;br>&lt;br>部分非固定的竖屏装饰（例如可在全屏模式下可隐藏的手机状态栏）和窗口装饰（例如标题栏或操作栏）不在考虑范围之内，因此应用必须准备好处理稍小于其指定值的空间。&lt;br>&lt;br>&lt;strong>注意&lt;/strong>：系统会选择宽度和高度均匹配的资源。因此，强烈建议您同时指定这两种资源，而不是仅指定其中一种资源。例如，如果实际屏幕为 w720dp 乘以 h1280dp，并且其中一个资源符合 w720dp 的条件，另一个资源符合 w700dp-h1200dp 的条件，即使前一个资源与其指定的资源完全匹配，系统也会选择后者。&lt;br>&lt;br>&lt;em>此项为 API 级别 13 中的新增配置。&lt;/em>&lt;br>&lt;br>另请参阅 &lt;code>[screenWidthDp](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#screenWidthDp)&lt;/code> 和 &lt;code>[screenHeightDp](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#screenHeightDp)&lt;/code> 配置字段，这些字段用于保存当前屏幕的宽度和高度。&lt;br>&lt;br>如需详细了解如何使用此限定符针对不同的屏幕进行设计，请参阅&lt;a href="https://developer.android.google.cn/guide/topics/large-screens/support-different-screen-sizes?hl=zh-cn">支持不同的屏幕尺寸&lt;/a>。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>屏幕尺寸&lt;/td>
 &lt;td>&lt;code>small&lt;/code> &lt;br>&lt;code>normal&lt;/code> &lt;br>&lt;code>large&lt;/code> &lt;br>&lt;code>xlarge&lt;/code>&lt;/td>
 &lt;td>- &lt;code>small&lt;/code>：尺寸类似于低密度 QVGA 屏幕的屏幕。小屏幕的最小布局尺寸约为 320x426 dp。例如，QVGA 低密度屏幕和 VGA 高密度屏幕。&lt;br>- &lt;code>normal&lt;/code>：尺寸类似于中等密度 HVGA 屏幕的屏幕。标准屏幕的最小布局尺寸约为 320x470 dp。例如，WQVGA 低密度屏幕、HVGA 中等密度屏幕和 WVGA 高密度屏幕。&lt;br>- &lt;code>large&lt;/code>：尺寸类似于中等密度 VGA 屏幕的屏幕。大屏幕的最小布局尺寸约为 480x640 dp。例如，VGA 和 WVGA 中等密度屏幕。&lt;br>- &lt;code>xlarge&lt;/code>：明显大于传统中等密度 HVGA 屏幕的屏幕。超大屏幕的最小布局尺寸约为 720x960 dp。在大多数情况下，屏幕超大的设备体积太大，不能放进口袋，最常见的是平板式设备。此项为 API 级别 9 中的新增配置。&lt;br>&lt;br>&lt;strong>注意&lt;/strong>：使用尺寸限定符并不意味着相应资源仅适用于该尺寸的屏幕。如果没有为备用资源提供最符合当前设备配置的限定符，则系统可以使用其中&lt;a href="https://developer.android.google.cn/guide/topics/resources/providing-resources?hl=zh-cn#BestMatch">最匹配&lt;/a>的资源。&lt;br>&lt;br>&lt;strong>注意：&lt;strong>如果所有资源均使用大于当前屏幕的尺寸限定符，则系统&lt;/strong>不会&lt;/strong>使用这些资源，并且应用会在运行时崩溃。例如，如果所有布局资源均以 &lt;code>xlarge&lt;/code> 限定符标记，但设备是标准尺寸的屏幕&lt;br>&lt;br>此项为 API 级别 4 中的新增配置。&lt;br>&lt;br>另请参阅 &lt;code>[screenLayout](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#screenLayout)&lt;/code> 配置字段，该字段用于指示屏幕为小尺寸、标准尺寸还是大尺寸。&lt;br>&lt;br>如需了解详情，请参阅&lt;a href="https://developer.android.google.cn/guide/practices/screens_support?hl=zh-cn">屏幕兼容性概览&lt;/a>。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>屏幕宽高比&lt;/td>
 &lt;td>&lt;code>long&lt;/code> &lt;br>&lt;code>notlong&lt;/code>&lt;/td>
 &lt;td>- &lt;code>long&lt;/code>：宽屏，如 WQVGA、WVGA、FWVGA&lt;br>- &lt;code>notlong&lt;/code>：非宽屏，如 QVGA、HVGA 和 VGA&lt;br>&lt;br>此项为 API 级别 4 中的新增配置。&lt;br>&lt;br>此配置完全基于屏幕的纵横比（&lt;code>long&lt;/code> 屏较宽），与屏幕方向无关。&lt;br>&lt;br>另请参阅 &lt;code>[screenLayout](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#screenLayout)&lt;/code> 配置字段，该字段用于指示屏幕是否为宽屏。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>圆形屏幕&lt;/td>
 &lt;td>&lt;code>round&lt;/code> &lt;br>&lt;code>notround&lt;/code>&lt;/td>
 &lt;td>- &lt;code>round&lt;/code>：圆形屏幕，例如圆形可穿戴式设备&lt;br>- &lt;code>notround&lt;/code>：方形屏幕，例如手机或平板电脑&lt;br>&lt;br>此项为 API 级别 23 中的新增配置。&lt;br>&lt;br>另请参阅 &lt;code>[isScreenRound()](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#isScreenRound())&lt;/code> 配置方法，该方法用于指示屏幕是否为圆形屏幕。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>广色域&lt;/td>
 &lt;td>&lt;code>widecg&lt;/code> &lt;br>&lt;code>nowidecg&lt;/code>&lt;/td>
 &lt;td>- &lt;code>widecg&lt;/code>：显示广色域，如 Display P3 或 AdobeRGB&lt;br>- &lt;code>nowidecg&lt;/code>：显示窄色域，如 sRGB&lt;br>&lt;br>此项为 API 级别 26 中的新增配置。&lt;br>&lt;br>另请参阅 &lt;code>[isScreenWideColorGamut()](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#isScreenWideColorGamut())&lt;/code> 配置方法，该方法用于指示屏幕是否具有广色域。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>高动态范围 (HDR)&lt;/td>
 &lt;td>&lt;code>highdr&lt;/code> &lt;br>&lt;code>lowdr&lt;/code>&lt;/td>
 &lt;td>- &lt;code>highdr&lt;/code>：显示高动态范围&lt;br>- &lt;code>lowdr&lt;/code>：显示低/标准动态范围&lt;br>&lt;br>此项为 API 级别 26 中的新增配置。&lt;br>&lt;br>另请参阅 &lt;code>[isScreenHdr()](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#isScreenHdr())&lt;/code> 配置方法，该方法用于指示屏幕是否具有 HDR 功能。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>屏幕方向&lt;/td>
 &lt;td>&lt;code>port&lt;/code> &lt;br>&lt;code>land&lt;/code>&lt;/td>
 &lt;td>- &lt;code>port&lt;/code>：设备处于竖屏模式（纵向）&lt;br>- &lt;code>land&lt;/code>：设备处于横屏模式（水平）&lt;br>&lt;br>如果用户旋转屏幕，此配置可能会在应用生命周期中发生变化。如需了解这会在运行时期间给应用带来哪些影响，请参阅&lt;a href="https://developer.android.google.cn/guide/topics/resources/runtime-changes?hl=zh-cn">处理配置更改&lt;/a>。&lt;br>&lt;br>另请参阅 &lt;code>[orientation](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#orientation)&lt;/code> 配置字段，该字段用于指示当前的设备方向。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>界面模式&lt;/td>
 &lt;td>&lt;code>car&lt;/code> &lt;br>&lt;code>desk&lt;/code> &lt;br>&lt;code>television&lt;/code> &lt;br>&lt;code>appliance&lt;/code> &lt;br>&lt;code>watch&lt;/code> &lt;br>&lt;code>vrheadset&lt;/code>&lt;/td>
 &lt;td>- &lt;code>car&lt;/code>：设备正在车载基座中显示&lt;br>- &lt;code>desk&lt;/code>：设备正在桌面基座中显示&lt;br>- &lt;code>television&lt;/code>：设备正在通过电视显示内容，通过将界面投影到离用户较远的大屏幕上，为用户提供“十英尺”体验。该体验主要面向遥控交互或其他非触控式交互&lt;br>- &lt;code>appliance&lt;/code>：设备用作不带显示屏的装置&lt;br>- &lt;code>watch&lt;/code>：设备配有显示屏，并且可戴在手腕上&lt;br>- &lt;code>vrheadset&lt;/code>：设备正在通过虚拟现实耳机显示内容&lt;br>&lt;br>此项为 API 级别 8 中的新增配置；API 13 中的新增电视配置；API 20 中的新增手表配置。&lt;br>&lt;br>如需了解在设备插入基座或从基座中取出时您的应用会如何响应，请参阅&lt;a href="https://developer.android.google.cn/training/monitoring-device-state/docking-monitoring?hl=zh-cn">确定和监控插接状态和基座类型&lt;/a>。&lt;br>&lt;br>如果用户将设备插入基座，此配置可能会在应用生命周期中发生变化。您可以使用 &lt;code>[UiModeManager](https://developer.android.google.cn/reference/android/app/UiModeManager?hl=zh-cn)&lt;/code> 启用或停用其中的部分模式。如需了解这会在运行时期间给应用带来哪些影响，请参阅&lt;a href="https://developer.android.google.cn/guide/topics/resources/runtime-changes?hl=zh-cn">处理配置更改&lt;/a>。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>夜间模式&lt;/td>
 &lt;td>&lt;code>night&lt;/code> &lt;br>&lt;code>notnight&lt;/code>&lt;/td>
 &lt;td>- &lt;code>night&lt;/code>：夜晚时光&lt;br>- &lt;code>notnight&lt;/code>：日间节目&lt;br>&lt;br>此项为 API 级别 8 中的新增配置。&lt;br>&lt;br>如果夜间模式停留在自动模式（默认），此配置可能会在应用生命周期中发生变化。在这种情况下，该模式会根据当天的时间进行调整。您可以使用 &lt;code>[UiModeManager](https://developer.android.google.cn/reference/android/app/UiModeManager?hl=zh-cn)&lt;/code> 启用或停用此模式。如需了解这会在运行时期间给应用带来哪些影响，请参阅&lt;a href="https://developer.android.google.cn/guide/topics/resources/runtime-changes?hl=zh-cn">处理配置更改&lt;/a>。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>屏幕像素密度 (dpi)&lt;/td>
 &lt;td>&lt;code>ldpi&lt;/code> &lt;br>&lt;code>mdpi&lt;/code> &lt;br>&lt;code>hdpi&lt;/code> &lt;br>&lt;code>xhdpi&lt;/code> &lt;br>&lt;code>xxhdpi&lt;/code> &lt;br>&lt;code>xxxhdpi&lt;/code> &lt;br>&lt;code>nodpi&lt;/code> &lt;br>&lt;code>tvdpi&lt;/code> &lt;br>&lt;code>anydpi&lt;/code> &lt;br>&lt;code>_nnn_dpi&lt;/code>&lt;/td>
 &lt;td>- &lt;code>ldpi&lt;/code>：低密度屏幕；约为 120 dpi。&lt;br>- &lt;code>mdpi&lt;/code>：中密度（传统 HVGA）屏幕；约为 160 dpi。&lt;br>- &lt;code>hdpi&lt;/code>：高密度屏幕；约为 240 dpi。&lt;br>- &lt;code>xhdpi&lt;/code>：超高密度屏幕；约为 320 dpi。此项为 API 级别 8 中的新增配置。&lt;br>- &lt;code>xxhdpi&lt;/code>：超超高密度屏幕；约为 480 dpi。此项为 API 级别 16 中的新增配置。&lt;br>- &lt;code>xxxhdpi&lt;/code>：超超超高密度屏幕使用（仅限启动器图标，请参阅&lt;a href="https://developer.android.google.cn/training/multiscreen/screendensities?hl=zh-cn">支持不同的像素密度&lt;/a>）；约为 640 dpi。此项为 API 级别 18 中的新增配置。&lt;br>- &lt;code>nodpi&lt;/code>：用于您不希望为匹配设备密度而进行缩放的位图资源。&lt;br>- &lt;code>tvdpi&lt;/code>：密度介于 mdpi 和 hdpi 之间的屏幕；约为 213 dpi。 这不属于“主要”密度组。它主要用于 720p 电视，而大多数应用都不需要它。对于 1080p 电视面板，请使用 &lt;code>xhdpi&lt;/code>；对于 4K 电视面板，请使用 &lt;code>xxxhdpi&lt;/code>。&lt;em>此项为 API 级别 13 中的新增配置。&lt;/em>&lt;br>- &lt;code>anydpi&lt;/code>：适合所有屏幕密度，其优先级高于其他限定符。这对于&lt;a href="https://developer.android.google.cn/training/material/drawables?hl=zh-cn#VectorDrawables">矢量可绘制对象&lt;/a>非常有用。此项为 API 级别 21 中的新增配置。&lt;br>- &lt;code>_nnn_dpi&lt;/code>：用于表示非标准密度，其中 &lt;code>_nnn_&lt;/code> 是正整数屏幕密度。大多数情况下不使用此限定符。使用标准密度存储分区，可显著减少因支持市场上各种设备屏幕密度而产生的开销。&lt;br>&lt;br>这六种主要密度之间的缩放比例为 3:4:6:8:12:16（tvdpi 密度忽略不计）。因此，9x9 (ldpi) 位图相当于 12x12 (mdpi)、18x18 (hdpi)、24x24 (xhdpi) 位图，依此类推。&lt;br>&lt;br>**注意：**使用密度限定符并不意味着资源仅适用于该密度的屏幕。如果您没有为备用资源提供最符合当前设备配置的限定符，则系统会使用其中&lt;a href="https://developer.android.google.cn/guide/topics/resources/providing-resources?hl=zh-cn#BestMatch">最匹配&lt;/a>的资源。&lt;br>&lt;br>如需详细了解如何处理不同屏幕密度以及 Android 如何缩放位图以适应当前密度，请参阅&lt;a href="https://developer.android.google.cn/guide/practices/screens_support?hl=zh-cn">屏幕兼容性概览&lt;/a>。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>触摸屏类型&lt;/td>
 &lt;td>&lt;code>notouch&lt;/code> &lt;br>&lt;code>finger&lt;/code>&lt;/td>
 &lt;td>- &lt;code>notouch&lt;/code>：设备没有触摸屏。&lt;br>- &lt;code>finger&lt;/code>：设备有一个专供用户通过手指直接与其交互的触摸屏。&lt;br>&lt;br>另请参阅 &lt;code>[touchscreen](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#touchscreen)&lt;/code> 配置字段，该字段用于指示设备的触摸屏类型。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>键盘可用性&lt;/td>
 &lt;td>&lt;code>keysexposed&lt;/code> &lt;br>&lt;code>keyshidden&lt;/code> &lt;br>&lt;code>keyssoft&lt;/code>&lt;/td>
 &lt;td>- &lt;code>keysexposed&lt;/code>：设备具有可用的键盘。如果设备启用了软件键盘（不无可能），那么即使用户未找到硬件键盘，或者该设备没有硬件键盘，也可使用此限定符。如果设备未提供软件键盘或软件键盘已停用，则只有在向用户公开硬件键盘时，才可使用此限定符。&lt;br>- &lt;code>keyshidden&lt;/code>：设备具有可用的硬件键盘，但其处于隐藏状态，且设备未启用软件键盘。&lt;br>- &lt;code>keyssoft&lt;/code>：设备已启用软件键盘（无论是否可见）。&lt;br>&lt;br>如果您提供了 &lt;code>keysexposed&lt;/code> 资源，但未提供 &lt;code>keyssoft&lt;/code> 资源，则无论键盘是否可见，只要系统已启用软件键盘，其便会使用 &lt;code>keysexposed&lt;/code> 资源。&lt;br>&lt;br>如果用户打开硬件键盘，此配置可能会在应用生命周期中发生变化。如需了解这会在运行时期间给应用带来哪些影响，请参阅&lt;a href="https://developer.android.google.cn/guide/topics/resources/runtime-changes?hl=zh-cn">处理配置更改&lt;/a>。&lt;br>&lt;br>另请参阅配置字段 &lt;code>[hardKeyboardHidden](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#hardKeyboardHidden)&lt;/code> 和 &lt;code>[keyboardHidden](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#keyboardHidden)&lt;/code>，这两个字段分别用于指示硬件键盘的可见性和任一键盘（包括软件键盘）的可见性。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>主要的文本输入法&lt;/td>
 &lt;td>&lt;code>nokeys&lt;/code> &lt;br>&lt;code>qwerty&lt;/code> &lt;br>&lt;code>12key&lt;/code>&lt;/td>
 &lt;td>- &lt;code>nokeys&lt;/code>：设备没有用于文本输入的硬件按键。&lt;br>- &lt;code>qwerty&lt;/code>：设备具有硬件 QWERTY 键盘（无论其对用户是否可见）。&lt;br>- &lt;code>12key&lt;/code>：设备具有包含 12 个按键的硬件键盘（无论其对用户是否可见）。&lt;br>&lt;br>另请参阅 &lt;code>[keyboard](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#keyboard)&lt;/code> 配置字段，该字段用于指示可用的主要文本输入法。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>导航键可用性&lt;/td>
 &lt;td>&lt;code>navexposed&lt;/code> &lt;br>&lt;code>navhidden&lt;/code>&lt;/td>
 &lt;td>- &lt;code>navexposed&lt;/code>：导航键可供用户使用。&lt;br>- &lt;code>navhidden&lt;/code>：导航键不可用（例如位于合上的盖子后面）。&lt;br>&lt;br>如果用户显示导航键，此配置可能会在应用生命周期中发生变化。如需了解这会在运行时期间给应用带来哪些影响，请参阅&lt;a href="https://developer.android.google.cn/guide/topics/resources/runtime-changes?hl=zh-cn">处理配置更改&lt;/a>。&lt;br>&lt;br>另请参阅 &lt;code>[navigationHidden](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#navigationHidden)&lt;/code> 配置字段，该字段用于指示导航键是否处于隐藏状态。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>主要的非触摸导航方法&lt;/td>
 &lt;td>&lt;code>nonav&lt;/code> &lt;br>&lt;code>dpad&lt;/code> &lt;br>&lt;code>trackball&lt;/code> &lt;br>&lt;code>wheel&lt;/code>&lt;/td>
 &lt;td>- &lt;code>nonav&lt;/code>：除了使用触摸屏以外，设备没有其他导航设施。&lt;br>- &lt;code>dpad&lt;/code>：设备具有用于导航的方向键。&lt;br>- &lt;code>trackball&lt;/code>：设备具有用于导航的轨迹球。&lt;br>- &lt;code>wheel&lt;/code>：设备具有用于导航方向盘（不常见）。&lt;br>&lt;br>另请参阅 &lt;code>[navigation](https://developer.android.google.cn/reference/android/content/res/Configuration?hl=zh-cn#navigation)&lt;/code> 配置字段，该字段用于指示可用的导航方法类型。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>平台版本（API 级别）&lt;/td>
 &lt;td>示例： &lt;br>&lt;code>v3&lt;/code> &lt;br>&lt;code>v4&lt;/code> &lt;br>&lt;code>v7&lt;/code> &lt;br>等等&lt;/td>
 &lt;td>设备支持的 API 级别。例如，&lt;code>v1&lt;/code> 对应 API 级别 1（搭载 Android 1.0 或更高版本的设备）；&lt;code>v4&lt;/code> 对应 API 级别 4（搭载 Android 1.6 或更高版本的设备）。如需了解有关这些值的详细信息，请参阅 &lt;a href="https://developer.android.google.cn/guide/topics/manifest/uses-sdk-element?hl=zh-cn#ApiLevels">Android API 级别&lt;/a>文档。&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div>&lt;p>&lt;strong>注意：&lt;/strong> 并非所有版本的 Android 系统都支持所有限定符。使用新限定符会隐式添加平台版本限定符，因此较旧版本系统的设备可以忽略它。例如，使用 &lt;code>w600dp&lt;/code> 限定符会自动包括 &lt;code>v13&lt;/code> 限定符，因为可用宽度限定符是 API 级别 13 中的新增配置。为避免出现任何问题，请始终包含一组默认资源（一组不包含任何限定符的资源）。如需了解详细信息，请参阅&lt;a href="https://developer.android.google.cn/guide/topics/resources/providing-resources?hl=zh-cn#Compatibility">利用资源提供最佳的设备兼容性&lt;/a>部分。&lt;/p>
&lt;h3 class="heading" id="限定符命名规则">
 限定符命名规则&lt;span class="heading__anchor"> &lt;a href="#%e9%99%90%e5%ae%9a%e7%ac%a6%e5%91%bd%e5%90%8d%e8%a7%84%e5%88%99">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>以下是一些使用配置限定符名称时需遵循的规则：&lt;/p>
&lt;ul>
&lt;li>您可以为单组资源指定多个限定符，并使用短划线分隔。例如，&lt;code>drawable-en-rUS-land&lt;/code> 适用于屏幕方向为横向且语言为美式英语的设备。&lt;/li>
&lt;li>这些限定符必须遵循&lt;a href="https://developer.android.google.cn/guide/topics/resources/providing-resources?hl=zh-cn#table2">表 2&lt;/a> 中列出的顺序。
&lt;ul>
&lt;li>错误：&lt;code>drawable-hdpi-port/&lt;/code>&lt;/li>
&lt;li>正确：&lt;code>drawable-port-hdpi/&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>不得嵌套备用资源目录。例如，您的目录不能为 &lt;code>res/drawable/drawable-en/&lt;/code>。&lt;/li>
&lt;li>值不区分大小写。资源编译器会在处理之前将目录名称转换为小写形式，以避免在不区分大小的文件系统中出现问题。名称中使用的所有大写字母只是为了便于认读。&lt;/li>
&lt;li>每种限定符类型仅支持一个值。例如，如需针对西班牙语和法语使用相同的可绘制对象文件，则您不能拥有名为 &lt;code>drawable-es-fr/&lt;/code> 的目录，而是需要两个包含相应文件的资源目录，如 &lt;code>drawable-es/&lt;/code> 和 &lt;code>drawable-fr/&lt;/code>。但是，您并不需要真的在这两个位置复制文件，而是可以为资源创建别名，如&lt;a href="https://developer.android.google.cn/guide/topics/resources/providing-resources?hl=zh-cn#AliasResources">创建别名资源&lt;/a>部分所述。&lt;/li>
&lt;/ul>
&lt;p>将备用资源保存到以这些限定符命名的目录后，Android 系统会基于当前设备配置自动使用您应用中的资源。在每次请求资源时，Android 系统会检查包含所请求资源文件的备用资源目录，然后&lt;a href="https://developer.android.google.cn/guide/topics/resources/providing-resources?hl=zh-cn#BestMatch">寻找最匹配的资源&lt;/a>。&lt;/p>
&lt;p>如果没有与特定设备配置匹配的备用资源，则 Android 系统会使用相应的默认资源（一组不包含配置限定符的特定类型的资源）。&lt;/p>
&lt;h3 class="heading" id="创建别名资源">
 创建别名资源&lt;span class="heading__anchor"> &lt;a href="#%e5%88%9b%e5%bb%ba%e5%88%ab%e5%90%8d%e8%b5%84%e6%ba%90">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>如果您想将某一资源用于多种设备配置，但不想以默认资源的形式提供该资源，则无需将同一资源放入多个备用资源目录中。不过，您可以创建备用资源，充当默认资源目录中所保存资源的别名。&lt;/p>
&lt;p>&lt;strong>注意&lt;/strong>：并非所有资源都提供能够为另一资源创建别名的机制。特别是，&lt;code>xml/&lt;/code> 目录中的动画资源、菜单资源、原始资源及其他未指定资源均不提供此功能。&lt;/p>
&lt;p>例如，假设您有一个应用图标 &lt;code>icon.png&lt;/code>，并且需要针对不同语言区域提供该图标的不同版本。但是，加拿大英语和加拿大法语这两种语言区域需使用同一版本。无需将同一图片复制到加拿大英语和加拿大法语的资源目录中。不过，您可以将这两种语言使用的图片保存为除 &lt;code>icon.png&lt;/code> 之外的任意名称（例如 &lt;code>icon_ca.png&lt;/code>），并将其放入默认的 &lt;code>res/drawable/&lt;/code> 目录中。然后，在 &lt;code>res/drawable-en-rCA/&lt;/code> 和 &lt;code>res/drawable-fr-rCA/&lt;/code> 中创建 &lt;code>icon.xml&lt;/code> 文件，使用 &lt;code>&amp;lt;bitmap&amp;gt;&lt;/code> 元素引用 &lt;code>icon_ca.png&lt;/code> 资源。这样您就可以只保存一个版本的 PNG 文件和两个指向该版本的小型 XML 文件。如需了解详情，请参阅以下部分中的示例。&lt;/p>
&lt;h4 class="heading" id="可绘制对象">
 可绘制对象&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%af%e7%bb%98%e5%88%b6%e5%af%b9%e8%b1%a1">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>如要为现有可绘制对象创建别名，请使用 &lt;code>&amp;lt;drawable&amp;gt;&lt;/code> 元素：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;resources&amp;gt;&lt;/span>    
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;drawable&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;icon&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>@drawable/icon_ca&lt;span class="nt">&amp;lt;/drawable&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/resources&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如果您将此文件保存为 &lt;code>icon.xml&lt;/code>并放入备用资源目录中（例如 &lt;code>res/values-en-rCA/&lt;/code>），那么它将被编译为可作为 &lt;code>R.drawable.icon&lt;/code> 引用的资源，但它实际上是 &lt;code>R.drawable.icon_ca&lt;/code> 资源（保存在 &lt;code>res/drawable/&lt;/code> 中）的别名。&lt;/p>
&lt;h4 class="heading" id="布局">
 布局&lt;span class="heading__anchor"> &lt;a href="#%e5%b8%83%e5%b1%80">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>如要为现有布局创建别名，请使用封装在 &lt;code>&amp;lt;merge&amp;gt;&lt;/code> 中的 &lt;code>&amp;lt;include&amp;gt;&lt;/code> 元素：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;merge&amp;gt;&lt;/span>    
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;include&lt;/span> &lt;span class="na">layout=&lt;/span>&lt;span class="s">&amp;#34;@layout/main_ltr&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/merge&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如果将此文件保存为 &lt;code>main.xml&lt;/code>，则系统会将其编译到可作为 &lt;code>R.layout.main&lt;/code> 引用的资源中，但该文件实际是 &lt;code>R.layout.main_ltr&lt;/code> 资源的别名。&lt;/p>
&lt;h4 class="heading" id="字符串和其他简单值">
 字符串和其他简单值&lt;span class="heading__anchor"> &lt;a href="#%e5%ad%97%e7%ac%a6%e4%b8%b2%e5%92%8c%e5%85%b6%e4%bb%96%e7%ae%80%e5%8d%95%e5%80%bc">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>如需为现有字符串创建别名，则可将所需字符串的资源 ID 用作新字符串的值：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;resources&amp;gt;&lt;/span>    
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;string&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;hello&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>Hello&lt;span class="nt">&amp;lt;/string&amp;gt;&lt;/span>    
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;string&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;hi&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>@string/hello&lt;span class="nt">&amp;lt;/string&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/resources&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>R.string.hi&lt;/code> 资源现在是 &lt;code>R.string.hello&lt;/code> 的别名。&lt;/p>
&lt;p>&lt;a href="https://developer.android.google.cn/guide/topics/resources/more-resources?hl=zh-cn">其他简单值&lt;/a>的原理相同，例如颜色：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;resources&amp;gt;&lt;/span>    
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;color&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;red&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>#f00&lt;span class="nt">&amp;lt;/color&amp;gt;&lt;/span>    
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;color&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;highlight&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>@color/red&lt;span class="nt">&amp;lt;/color&amp;gt;&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/resources&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>original link:&lt;a href="https://developer.android.google.cn/guide/topics/resources/providing-resources?hl=zh-cn#AlternativeResources">应用资源概览  |  Android 开发者  |  Android Developers&lt;/a>&lt;/p></description></item><item><title>Zip File Format</title><link>https://opoojkk.com/posts/2024/03/17/zip-file-format/</link><pubDate>Sun, 17 Mar 2024 09:13:47 +0800</pubDate><guid>https://opoojkk.com/posts/2024/03/17/zip-file-format/</guid><description>&lt;h2 class="heading" id="来源">
 来源&lt;span class="heading__anchor"> &lt;a href="#%e6%9d%a5%e6%ba%90">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>从&lt;a href="https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT">APPNOTE.TXT - .ZIP File Format Specification&lt;/a>找来备份，以防需要时找不到。&lt;/p>
&lt;h2 class="heading" id="内容">
 内容&lt;span class="heading__anchor"> &lt;a href="#%e5%86%85%e5%ae%b9">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>内容有些长😄。&lt;/p>
&lt;blockquote>
&lt;p>很奇怪，排板总是有些问题。&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt"> 100
&lt;/span>&lt;span class="lnt"> 101
&lt;/span>&lt;span class="lnt"> 102
&lt;/span>&lt;span class="lnt"> 103
&lt;/span>&lt;span class="lnt"> 104
&lt;/span>&lt;span class="lnt"> 105
&lt;/span>&lt;span class="lnt"> 106
&lt;/span>&lt;span class="lnt"> 107
&lt;/span>&lt;span class="lnt"> 108
&lt;/span>&lt;span class="lnt"> 109
&lt;/span>&lt;span class="lnt"> 110
&lt;/span>&lt;span class="lnt"> 111
&lt;/span>&lt;span class="lnt"> 112
&lt;/span>&lt;span class="lnt"> 113
&lt;/span>&lt;span class="lnt"> 114
&lt;/span>&lt;span class="lnt"> 115
&lt;/span>&lt;span class="lnt"> 116
&lt;/span>&lt;span class="lnt"> 117
&lt;/span>&lt;span class="lnt"> 118
&lt;/span>&lt;span class="lnt"> 119
&lt;/span>&lt;span class="lnt"> 120
&lt;/span>&lt;span class="lnt"> 121
&lt;/span>&lt;span class="lnt"> 122
&lt;/span>&lt;span class="lnt"> 123
&lt;/span>&lt;span class="lnt"> 124
&lt;/span>&lt;span class="lnt"> 125
&lt;/span>&lt;span class="lnt"> 126
&lt;/span>&lt;span class="lnt"> 127
&lt;/span>&lt;span class="lnt"> 128
&lt;/span>&lt;span class="lnt"> 129
&lt;/span>&lt;span class="lnt"> 130
&lt;/span>&lt;span class="lnt"> 131
&lt;/span>&lt;span class="lnt"> 132
&lt;/span>&lt;span class="lnt"> 133
&lt;/span>&lt;span class="lnt"> 134
&lt;/span>&lt;span class="lnt"> 135
&lt;/span>&lt;span class="lnt"> 136
&lt;/span>&lt;span class="lnt"> 137
&lt;/span>&lt;span class="lnt"> 138
&lt;/span>&lt;span class="lnt"> 139
&lt;/span>&lt;span class="lnt"> 140
&lt;/span>&lt;span class="lnt"> 141
&lt;/span>&lt;span class="lnt"> 142
&lt;/span>&lt;span class="lnt"> 143
&lt;/span>&lt;span class="lnt"> 144
&lt;/span>&lt;span class="lnt"> 145
&lt;/span>&lt;span class="lnt"> 146
&lt;/span>&lt;span class="lnt"> 147
&lt;/span>&lt;span class="lnt"> 148
&lt;/span>&lt;span class="lnt"> 149
&lt;/span>&lt;span class="lnt"> 150
&lt;/span>&lt;span class="lnt"> 151
&lt;/span>&lt;span class="lnt"> 152
&lt;/span>&lt;span class="lnt"> 153
&lt;/span>&lt;span class="lnt"> 154
&lt;/span>&lt;span class="lnt"> 155
&lt;/span>&lt;span class="lnt"> 156
&lt;/span>&lt;span class="lnt"> 157
&lt;/span>&lt;span class="lnt"> 158
&lt;/span>&lt;span class="lnt"> 159
&lt;/span>&lt;span class="lnt"> 160
&lt;/span>&lt;span class="lnt"> 161
&lt;/span>&lt;span class="lnt"> 162
&lt;/span>&lt;span class="lnt"> 163
&lt;/span>&lt;span class="lnt"> 164
&lt;/span>&lt;span class="lnt"> 165
&lt;/span>&lt;span class="lnt"> 166
&lt;/span>&lt;span class="lnt"> 167
&lt;/span>&lt;span class="lnt"> 168
&lt;/span>&lt;span class="lnt"> 169
&lt;/span>&lt;span class="lnt"> 170
&lt;/span>&lt;span class="lnt"> 171
&lt;/span>&lt;span class="lnt"> 172
&lt;/span>&lt;span class="lnt"> 173
&lt;/span>&lt;span class="lnt"> 174
&lt;/span>&lt;span class="lnt"> 175
&lt;/span>&lt;span class="lnt"> 176
&lt;/span>&lt;span class="lnt"> 177
&lt;/span>&lt;span class="lnt"> 178
&lt;/span>&lt;span class="lnt"> 179
&lt;/span>&lt;span class="lnt"> 180
&lt;/span>&lt;span class="lnt"> 181
&lt;/span>&lt;span class="lnt"> 182
&lt;/span>&lt;span class="lnt"> 183
&lt;/span>&lt;span class="lnt"> 184
&lt;/span>&lt;span class="lnt"> 185
&lt;/span>&lt;span class="lnt"> 186
&lt;/span>&lt;span class="lnt"> 187
&lt;/span>&lt;span class="lnt"> 188
&lt;/span>&lt;span class="lnt"> 189
&lt;/span>&lt;span class="lnt"> 190
&lt;/span>&lt;span class="lnt"> 191
&lt;/span>&lt;span class="lnt"> 192
&lt;/span>&lt;span class="lnt"> 193
&lt;/span>&lt;span class="lnt"> 194
&lt;/span>&lt;span class="lnt"> 195
&lt;/span>&lt;span class="lnt"> 196
&lt;/span>&lt;span class="lnt"> 197
&lt;/span>&lt;span class="lnt"> 198
&lt;/span>&lt;span class="lnt"> 199
&lt;/span>&lt;span class="lnt"> 200
&lt;/span>&lt;span class="lnt"> 201
&lt;/span>&lt;span class="lnt"> 202
&lt;/span>&lt;span class="lnt"> 203
&lt;/span>&lt;span class="lnt"> 204
&lt;/span>&lt;span class="lnt"> 205
&lt;/span>&lt;span class="lnt"> 206
&lt;/span>&lt;span class="lnt"> 207
&lt;/span>&lt;span class="lnt"> 208
&lt;/span>&lt;span class="lnt"> 209
&lt;/span>&lt;span class="lnt"> 210
&lt;/span>&lt;span class="lnt"> 211
&lt;/span>&lt;span class="lnt"> 212
&lt;/span>&lt;span class="lnt"> 213
&lt;/span>&lt;span class="lnt"> 214
&lt;/span>&lt;span class="lnt"> 215
&lt;/span>&lt;span class="lnt"> 216
&lt;/span>&lt;span class="lnt"> 217
&lt;/span>&lt;span class="lnt"> 218
&lt;/span>&lt;span class="lnt"> 219
&lt;/span>&lt;span class="lnt"> 220
&lt;/span>&lt;span class="lnt"> 221
&lt;/span>&lt;span class="lnt"> 222
&lt;/span>&lt;span class="lnt"> 223
&lt;/span>&lt;span class="lnt"> 224
&lt;/span>&lt;span class="lnt"> 225
&lt;/span>&lt;span class="lnt"> 226
&lt;/span>&lt;span class="lnt"> 227
&lt;/span>&lt;span class="lnt"> 228
&lt;/span>&lt;span class="lnt"> 229
&lt;/span>&lt;span class="lnt"> 230
&lt;/span>&lt;span class="lnt"> 231
&lt;/span>&lt;span class="lnt"> 232
&lt;/span>&lt;span class="lnt"> 233
&lt;/span>&lt;span class="lnt"> 234
&lt;/span>&lt;span class="lnt"> 235
&lt;/span>&lt;span class="lnt"> 236
&lt;/span>&lt;span class="lnt"> 237
&lt;/span>&lt;span class="lnt"> 238
&lt;/span>&lt;span class="lnt"> 239
&lt;/span>&lt;span class="lnt"> 240
&lt;/span>&lt;span class="lnt"> 241
&lt;/span>&lt;span class="lnt"> 242
&lt;/span>&lt;span class="lnt"> 243
&lt;/span>&lt;span class="lnt"> 244
&lt;/span>&lt;span class="lnt"> 245
&lt;/span>&lt;span class="lnt"> 246
&lt;/span>&lt;span class="lnt"> 247
&lt;/span>&lt;span class="lnt"> 248
&lt;/span>&lt;span class="lnt"> 249
&lt;/span>&lt;span class="lnt"> 250
&lt;/span>&lt;span class="lnt"> 251
&lt;/span>&lt;span class="lnt"> 252
&lt;/span>&lt;span class="lnt"> 253
&lt;/span>&lt;span class="lnt"> 254
&lt;/span>&lt;span class="lnt"> 255
&lt;/span>&lt;span class="lnt"> 256
&lt;/span>&lt;span class="lnt"> 257
&lt;/span>&lt;span class="lnt"> 258
&lt;/span>&lt;span class="lnt"> 259
&lt;/span>&lt;span class="lnt"> 260
&lt;/span>&lt;span class="lnt"> 261
&lt;/span>&lt;span class="lnt"> 262
&lt;/span>&lt;span class="lnt"> 263
&lt;/span>&lt;span class="lnt"> 264
&lt;/span>&lt;span class="lnt"> 265
&lt;/span>&lt;span class="lnt"> 266
&lt;/span>&lt;span class="lnt"> 267
&lt;/span>&lt;span class="lnt"> 268
&lt;/span>&lt;span class="lnt"> 269
&lt;/span>&lt;span class="lnt"> 270
&lt;/span>&lt;span class="lnt"> 271
&lt;/span>&lt;span class="lnt"> 272
&lt;/span>&lt;span class="lnt"> 273
&lt;/span>&lt;span class="lnt"> 274
&lt;/span>&lt;span class="lnt"> 275
&lt;/span>&lt;span class="lnt"> 276
&lt;/span>&lt;span class="lnt"> 277
&lt;/span>&lt;span class="lnt"> 278
&lt;/span>&lt;span class="lnt"> 279
&lt;/span>&lt;span class="lnt"> 280
&lt;/span>&lt;span class="lnt"> 281
&lt;/span>&lt;span class="lnt"> 282
&lt;/span>&lt;span class="lnt"> 283
&lt;/span>&lt;span class="lnt"> 284
&lt;/span>&lt;span class="lnt"> 285
&lt;/span>&lt;span class="lnt"> 286
&lt;/span>&lt;span class="lnt"> 287
&lt;/span>&lt;span class="lnt"> 288
&lt;/span>&lt;span class="lnt"> 289
&lt;/span>&lt;span class="lnt"> 290
&lt;/span>&lt;span class="lnt"> 291
&lt;/span>&lt;span class="lnt"> 292
&lt;/span>&lt;span class="lnt"> 293
&lt;/span>&lt;span class="lnt"> 294
&lt;/span>&lt;span class="lnt"> 295
&lt;/span>&lt;span class="lnt"> 296
&lt;/span>&lt;span class="lnt"> 297
&lt;/span>&lt;span class="lnt"> 298
&lt;/span>&lt;span class="lnt"> 299
&lt;/span>&lt;span class="lnt"> 300
&lt;/span>&lt;span class="lnt"> 301
&lt;/span>&lt;span class="lnt"> 302
&lt;/span>&lt;span class="lnt"> 303
&lt;/span>&lt;span class="lnt"> 304
&lt;/span>&lt;span class="lnt"> 305
&lt;/span>&lt;span class="lnt"> 306
&lt;/span>&lt;span class="lnt"> 307
&lt;/span>&lt;span class="lnt"> 308
&lt;/span>&lt;span class="lnt"> 309
&lt;/span>&lt;span class="lnt"> 310
&lt;/span>&lt;span class="lnt"> 311
&lt;/span>&lt;span class="lnt"> 312
&lt;/span>&lt;span class="lnt"> 313
&lt;/span>&lt;span class="lnt"> 314
&lt;/span>&lt;span class="lnt"> 315
&lt;/span>&lt;span class="lnt"> 316
&lt;/span>&lt;span class="lnt"> 317
&lt;/span>&lt;span class="lnt"> 318
&lt;/span>&lt;span class="lnt"> 319
&lt;/span>&lt;span class="lnt"> 320
&lt;/span>&lt;span class="lnt"> 321
&lt;/span>&lt;span class="lnt"> 322
&lt;/span>&lt;span class="lnt"> 323
&lt;/span>&lt;span class="lnt"> 324
&lt;/span>&lt;span class="lnt"> 325
&lt;/span>&lt;span class="lnt"> 326
&lt;/span>&lt;span class="lnt"> 327
&lt;/span>&lt;span class="lnt"> 328
&lt;/span>&lt;span class="lnt"> 329
&lt;/span>&lt;span class="lnt"> 330
&lt;/span>&lt;span class="lnt"> 331
&lt;/span>&lt;span class="lnt"> 332
&lt;/span>&lt;span class="lnt"> 333
&lt;/span>&lt;span class="lnt"> 334
&lt;/span>&lt;span class="lnt"> 335
&lt;/span>&lt;span class="lnt"> 336
&lt;/span>&lt;span class="lnt"> 337
&lt;/span>&lt;span class="lnt"> 338
&lt;/span>&lt;span class="lnt"> 339
&lt;/span>&lt;span class="lnt"> 340
&lt;/span>&lt;span class="lnt"> 341
&lt;/span>&lt;span class="lnt"> 342
&lt;/span>&lt;span class="lnt"> 343
&lt;/span>&lt;span class="lnt"> 344
&lt;/span>&lt;span class="lnt"> 345
&lt;/span>&lt;span class="lnt"> 346
&lt;/span>&lt;span class="lnt"> 347
&lt;/span>&lt;span class="lnt"> 348
&lt;/span>&lt;span class="lnt"> 349
&lt;/span>&lt;span class="lnt"> 350
&lt;/span>&lt;span class="lnt"> 351
&lt;/span>&lt;span class="lnt"> 352
&lt;/span>&lt;span class="lnt"> 353
&lt;/span>&lt;span class="lnt"> 354
&lt;/span>&lt;span class="lnt"> 355
&lt;/span>&lt;span class="lnt"> 356
&lt;/span>&lt;span class="lnt"> 357
&lt;/span>&lt;span class="lnt"> 358
&lt;/span>&lt;span class="lnt"> 359
&lt;/span>&lt;span class="lnt"> 360
&lt;/span>&lt;span class="lnt"> 361
&lt;/span>&lt;span class="lnt"> 362
&lt;/span>&lt;span class="lnt"> 363
&lt;/span>&lt;span class="lnt"> 364
&lt;/span>&lt;span class="lnt"> 365
&lt;/span>&lt;span class="lnt"> 366
&lt;/span>&lt;span class="lnt"> 367
&lt;/span>&lt;span class="lnt"> 368
&lt;/span>&lt;span class="lnt"> 369
&lt;/span>&lt;span class="lnt"> 370
&lt;/span>&lt;span class="lnt"> 371
&lt;/span>&lt;span class="lnt"> 372
&lt;/span>&lt;span class="lnt"> 373
&lt;/span>&lt;span class="lnt"> 374
&lt;/span>&lt;span class="lnt"> 375
&lt;/span>&lt;span class="lnt"> 376
&lt;/span>&lt;span class="lnt"> 377
&lt;/span>&lt;span class="lnt"> 378
&lt;/span>&lt;span class="lnt"> 379
&lt;/span>&lt;span class="lnt"> 380
&lt;/span>&lt;span class="lnt"> 381
&lt;/span>&lt;span class="lnt"> 382
&lt;/span>&lt;span class="lnt"> 383
&lt;/span>&lt;span class="lnt"> 384
&lt;/span>&lt;span class="lnt"> 385
&lt;/span>&lt;span class="lnt"> 386
&lt;/span>&lt;span class="lnt"> 387
&lt;/span>&lt;span class="lnt"> 388
&lt;/span>&lt;span class="lnt"> 389
&lt;/span>&lt;span class="lnt"> 390
&lt;/span>&lt;span class="lnt"> 391
&lt;/span>&lt;span class="lnt"> 392
&lt;/span>&lt;span class="lnt"> 393
&lt;/span>&lt;span class="lnt"> 394
&lt;/span>&lt;span class="lnt"> 395
&lt;/span>&lt;span class="lnt"> 396
&lt;/span>&lt;span class="lnt"> 397
&lt;/span>&lt;span class="lnt"> 398
&lt;/span>&lt;span class="lnt"> 399
&lt;/span>&lt;span class="lnt"> 400
&lt;/span>&lt;span class="lnt"> 401
&lt;/span>&lt;span class="lnt"> 402
&lt;/span>&lt;span class="lnt"> 403
&lt;/span>&lt;span class="lnt"> 404
&lt;/span>&lt;span class="lnt"> 405
&lt;/span>&lt;span class="lnt"> 406
&lt;/span>&lt;span class="lnt"> 407
&lt;/span>&lt;span class="lnt"> 408
&lt;/span>&lt;span class="lnt"> 409
&lt;/span>&lt;span class="lnt"> 410
&lt;/span>&lt;span class="lnt"> 411
&lt;/span>&lt;span class="lnt"> 412
&lt;/span>&lt;span class="lnt"> 413
&lt;/span>&lt;span class="lnt"> 414
&lt;/span>&lt;span class="lnt"> 415
&lt;/span>&lt;span class="lnt"> 416
&lt;/span>&lt;span class="lnt"> 417
&lt;/span>&lt;span class="lnt"> 418
&lt;/span>&lt;span class="lnt"> 419
&lt;/span>&lt;span class="lnt"> 420
&lt;/span>&lt;span class="lnt"> 421
&lt;/span>&lt;span class="lnt"> 422
&lt;/span>&lt;span class="lnt"> 423
&lt;/span>&lt;span class="lnt"> 424
&lt;/span>&lt;span class="lnt"> 425
&lt;/span>&lt;span class="lnt"> 426
&lt;/span>&lt;span class="lnt"> 427
&lt;/span>&lt;span class="lnt"> 428
&lt;/span>&lt;span class="lnt"> 429
&lt;/span>&lt;span class="lnt"> 430
&lt;/span>&lt;span class="lnt"> 431
&lt;/span>&lt;span class="lnt"> 432
&lt;/span>&lt;span class="lnt"> 433
&lt;/span>&lt;span class="lnt"> 434
&lt;/span>&lt;span class="lnt"> 435
&lt;/span>&lt;span class="lnt"> 436
&lt;/span>&lt;span class="lnt"> 437
&lt;/span>&lt;span class="lnt"> 438
&lt;/span>&lt;span class="lnt"> 439
&lt;/span>&lt;span class="lnt"> 440
&lt;/span>&lt;span class="lnt"> 441
&lt;/span>&lt;span class="lnt"> 442
&lt;/span>&lt;span class="lnt"> 443
&lt;/span>&lt;span class="lnt"> 444
&lt;/span>&lt;span class="lnt"> 445
&lt;/span>&lt;span class="lnt"> 446
&lt;/span>&lt;span class="lnt"> 447
&lt;/span>&lt;span class="lnt"> 448
&lt;/span>&lt;span class="lnt"> 449
&lt;/span>&lt;span class="lnt"> 450
&lt;/span>&lt;span class="lnt"> 451
&lt;/span>&lt;span class="lnt"> 452
&lt;/span>&lt;span class="lnt"> 453
&lt;/span>&lt;span class="lnt"> 454
&lt;/span>&lt;span class="lnt"> 455
&lt;/span>&lt;span class="lnt"> 456
&lt;/span>&lt;span class="lnt"> 457
&lt;/span>&lt;span class="lnt"> 458
&lt;/span>&lt;span class="lnt"> 459
&lt;/span>&lt;span class="lnt"> 460
&lt;/span>&lt;span class="lnt"> 461
&lt;/span>&lt;span class="lnt"> 462
&lt;/span>&lt;span class="lnt"> 463
&lt;/span>&lt;span class="lnt"> 464
&lt;/span>&lt;span class="lnt"> 465
&lt;/span>&lt;span class="lnt"> 466
&lt;/span>&lt;span class="lnt"> 467
&lt;/span>&lt;span class="lnt"> 468
&lt;/span>&lt;span class="lnt"> 469
&lt;/span>&lt;span class="lnt"> 470
&lt;/span>&lt;span class="lnt"> 471
&lt;/span>&lt;span class="lnt"> 472
&lt;/span>&lt;span class="lnt"> 473
&lt;/span>&lt;span class="lnt"> 474
&lt;/span>&lt;span class="lnt"> 475
&lt;/span>&lt;span class="lnt"> 476
&lt;/span>&lt;span class="lnt"> 477
&lt;/span>&lt;span class="lnt"> 478
&lt;/span>&lt;span class="lnt"> 479
&lt;/span>&lt;span class="lnt"> 480
&lt;/span>&lt;span class="lnt"> 481
&lt;/span>&lt;span class="lnt"> 482
&lt;/span>&lt;span class="lnt"> 483
&lt;/span>&lt;span class="lnt"> 484
&lt;/span>&lt;span class="lnt"> 485
&lt;/span>&lt;span class="lnt"> 486
&lt;/span>&lt;span class="lnt"> 487
&lt;/span>&lt;span class="lnt"> 488
&lt;/span>&lt;span class="lnt"> 489
&lt;/span>&lt;span class="lnt"> 490
&lt;/span>&lt;span class="lnt"> 491
&lt;/span>&lt;span class="lnt"> 492
&lt;/span>&lt;span class="lnt"> 493
&lt;/span>&lt;span class="lnt"> 494
&lt;/span>&lt;span class="lnt"> 495
&lt;/span>&lt;span class="lnt"> 496
&lt;/span>&lt;span class="lnt"> 497
&lt;/span>&lt;span class="lnt"> 498
&lt;/span>&lt;span class="lnt"> 499
&lt;/span>&lt;span class="lnt"> 500
&lt;/span>&lt;span class="lnt"> 501
&lt;/span>&lt;span class="lnt"> 502
&lt;/span>&lt;span class="lnt"> 503
&lt;/span>&lt;span class="lnt"> 504
&lt;/span>&lt;span class="lnt"> 505
&lt;/span>&lt;span class="lnt"> 506
&lt;/span>&lt;span class="lnt"> 507
&lt;/span>&lt;span class="lnt"> 508
&lt;/span>&lt;span class="lnt"> 509
&lt;/span>&lt;span class="lnt"> 510
&lt;/span>&lt;span class="lnt"> 511
&lt;/span>&lt;span class="lnt"> 512
&lt;/span>&lt;span class="lnt"> 513
&lt;/span>&lt;span class="lnt"> 514
&lt;/span>&lt;span class="lnt"> 515
&lt;/span>&lt;span class="lnt"> 516
&lt;/span>&lt;span class="lnt"> 517
&lt;/span>&lt;span class="lnt"> 518
&lt;/span>&lt;span class="lnt"> 519
&lt;/span>&lt;span class="lnt"> 520
&lt;/span>&lt;span class="lnt"> 521
&lt;/span>&lt;span class="lnt"> 522
&lt;/span>&lt;span class="lnt"> 523
&lt;/span>&lt;span class="lnt"> 524
&lt;/span>&lt;span class="lnt"> 525
&lt;/span>&lt;span class="lnt"> 526
&lt;/span>&lt;span class="lnt"> 527
&lt;/span>&lt;span class="lnt"> 528
&lt;/span>&lt;span class="lnt"> 529
&lt;/span>&lt;span class="lnt"> 530
&lt;/span>&lt;span class="lnt"> 531
&lt;/span>&lt;span class="lnt"> 532
&lt;/span>&lt;span class="lnt"> 533
&lt;/span>&lt;span class="lnt"> 534
&lt;/span>&lt;span class="lnt"> 535
&lt;/span>&lt;span class="lnt"> 536
&lt;/span>&lt;span class="lnt"> 537
&lt;/span>&lt;span class="lnt"> 538
&lt;/span>&lt;span class="lnt"> 539
&lt;/span>&lt;span class="lnt"> 540
&lt;/span>&lt;span class="lnt"> 541
&lt;/span>&lt;span class="lnt"> 542
&lt;/span>&lt;span class="lnt"> 543
&lt;/span>&lt;span class="lnt"> 544
&lt;/span>&lt;span class="lnt"> 545
&lt;/span>&lt;span class="lnt"> 546
&lt;/span>&lt;span class="lnt"> 547
&lt;/span>&lt;span class="lnt"> 548
&lt;/span>&lt;span class="lnt"> 549
&lt;/span>&lt;span class="lnt"> 550
&lt;/span>&lt;span class="lnt"> 551
&lt;/span>&lt;span class="lnt"> 552
&lt;/span>&lt;span class="lnt"> 553
&lt;/span>&lt;span class="lnt"> 554
&lt;/span>&lt;span class="lnt"> 555
&lt;/span>&lt;span class="lnt"> 556
&lt;/span>&lt;span class="lnt"> 557
&lt;/span>&lt;span class="lnt"> 558
&lt;/span>&lt;span class="lnt"> 559
&lt;/span>&lt;span class="lnt"> 560
&lt;/span>&lt;span class="lnt"> 561
&lt;/span>&lt;span class="lnt"> 562
&lt;/span>&lt;span class="lnt"> 563
&lt;/span>&lt;span class="lnt"> 564
&lt;/span>&lt;span class="lnt"> 565
&lt;/span>&lt;span class="lnt"> 566
&lt;/span>&lt;span class="lnt"> 567
&lt;/span>&lt;span class="lnt"> 568
&lt;/span>&lt;span class="lnt"> 569
&lt;/span>&lt;span class="lnt"> 570
&lt;/span>&lt;span class="lnt"> 571
&lt;/span>&lt;span class="lnt"> 572
&lt;/span>&lt;span class="lnt"> 573
&lt;/span>&lt;span class="lnt"> 574
&lt;/span>&lt;span class="lnt"> 575
&lt;/span>&lt;span class="lnt"> 576
&lt;/span>&lt;span class="lnt"> 577
&lt;/span>&lt;span class="lnt"> 578
&lt;/span>&lt;span class="lnt"> 579
&lt;/span>&lt;span class="lnt"> 580
&lt;/span>&lt;span class="lnt"> 581
&lt;/span>&lt;span class="lnt"> 582
&lt;/span>&lt;span class="lnt"> 583
&lt;/span>&lt;span class="lnt"> 584
&lt;/span>&lt;span class="lnt"> 585
&lt;/span>&lt;span class="lnt"> 586
&lt;/span>&lt;span class="lnt"> 587
&lt;/span>&lt;span class="lnt"> 588
&lt;/span>&lt;span class="lnt"> 589
&lt;/span>&lt;span class="lnt"> 590
&lt;/span>&lt;span class="lnt"> 591
&lt;/span>&lt;span class="lnt"> 592
&lt;/span>&lt;span class="lnt"> 593
&lt;/span>&lt;span class="lnt"> 594
&lt;/span>&lt;span class="lnt"> 595
&lt;/span>&lt;span class="lnt"> 596
&lt;/span>&lt;span class="lnt"> 597
&lt;/span>&lt;span class="lnt"> 598
&lt;/span>&lt;span class="lnt"> 599
&lt;/span>&lt;span class="lnt"> 600
&lt;/span>&lt;span class="lnt"> 601
&lt;/span>&lt;span class="lnt"> 602
&lt;/span>&lt;span class="lnt"> 603
&lt;/span>&lt;span class="lnt"> 604
&lt;/span>&lt;span class="lnt"> 605
&lt;/span>&lt;span class="lnt"> 606
&lt;/span>&lt;span class="lnt"> 607
&lt;/span>&lt;span class="lnt"> 608
&lt;/span>&lt;span class="lnt"> 609
&lt;/span>&lt;span class="lnt"> 610
&lt;/span>&lt;span class="lnt"> 611
&lt;/span>&lt;span class="lnt"> 612
&lt;/span>&lt;span class="lnt"> 613
&lt;/span>&lt;span class="lnt"> 614
&lt;/span>&lt;span class="lnt"> 615
&lt;/span>&lt;span class="lnt"> 616
&lt;/span>&lt;span class="lnt"> 617
&lt;/span>&lt;span class="lnt"> 618
&lt;/span>&lt;span class="lnt"> 619
&lt;/span>&lt;span class="lnt"> 620
&lt;/span>&lt;span class="lnt"> 621
&lt;/span>&lt;span class="lnt"> 622
&lt;/span>&lt;span class="lnt"> 623
&lt;/span>&lt;span class="lnt"> 624
&lt;/span>&lt;span class="lnt"> 625
&lt;/span>&lt;span class="lnt"> 626
&lt;/span>&lt;span class="lnt"> 627
&lt;/span>&lt;span class="lnt"> 628
&lt;/span>&lt;span class="lnt"> 629
&lt;/span>&lt;span class="lnt"> 630
&lt;/span>&lt;span class="lnt"> 631
&lt;/span>&lt;span class="lnt"> 632
&lt;/span>&lt;span class="lnt"> 633
&lt;/span>&lt;span class="lnt"> 634
&lt;/span>&lt;span class="lnt"> 635
&lt;/span>&lt;span class="lnt"> 636
&lt;/span>&lt;span class="lnt"> 637
&lt;/span>&lt;span class="lnt"> 638
&lt;/span>&lt;span class="lnt"> 639
&lt;/span>&lt;span class="lnt"> 640
&lt;/span>&lt;span class="lnt"> 641
&lt;/span>&lt;span class="lnt"> 642
&lt;/span>&lt;span class="lnt"> 643
&lt;/span>&lt;span class="lnt"> 644
&lt;/span>&lt;span class="lnt"> 645
&lt;/span>&lt;span class="lnt"> 646
&lt;/span>&lt;span class="lnt"> 647
&lt;/span>&lt;span class="lnt"> 648
&lt;/span>&lt;span class="lnt"> 649
&lt;/span>&lt;span class="lnt"> 650
&lt;/span>&lt;span class="lnt"> 651
&lt;/span>&lt;span class="lnt"> 652
&lt;/span>&lt;span class="lnt"> 653
&lt;/span>&lt;span class="lnt"> 654
&lt;/span>&lt;span class="lnt"> 655
&lt;/span>&lt;span class="lnt"> 656
&lt;/span>&lt;span class="lnt"> 657
&lt;/span>&lt;span class="lnt"> 658
&lt;/span>&lt;span class="lnt"> 659
&lt;/span>&lt;span class="lnt"> 660
&lt;/span>&lt;span class="lnt"> 661
&lt;/span>&lt;span class="lnt"> 662
&lt;/span>&lt;span class="lnt"> 663
&lt;/span>&lt;span class="lnt"> 664
&lt;/span>&lt;span class="lnt"> 665
&lt;/span>&lt;span class="lnt"> 666
&lt;/span>&lt;span class="lnt"> 667
&lt;/span>&lt;span class="lnt"> 668
&lt;/span>&lt;span class="lnt"> 669
&lt;/span>&lt;span class="lnt"> 670
&lt;/span>&lt;span class="lnt"> 671
&lt;/span>&lt;span class="lnt"> 672
&lt;/span>&lt;span class="lnt"> 673
&lt;/span>&lt;span class="lnt"> 674
&lt;/span>&lt;span class="lnt"> 675
&lt;/span>&lt;span class="lnt"> 676
&lt;/span>&lt;span class="lnt"> 677
&lt;/span>&lt;span class="lnt"> 678
&lt;/span>&lt;span class="lnt"> 679
&lt;/span>&lt;span class="lnt"> 680
&lt;/span>&lt;span class="lnt"> 681
&lt;/span>&lt;span class="lnt"> 682
&lt;/span>&lt;span class="lnt"> 683
&lt;/span>&lt;span class="lnt"> 684
&lt;/span>&lt;span class="lnt"> 685
&lt;/span>&lt;span class="lnt"> 686
&lt;/span>&lt;span class="lnt"> 687
&lt;/span>&lt;span class="lnt"> 688
&lt;/span>&lt;span class="lnt"> 689
&lt;/span>&lt;span class="lnt"> 690
&lt;/span>&lt;span class="lnt"> 691
&lt;/span>&lt;span class="lnt"> 692
&lt;/span>&lt;span class="lnt"> 693
&lt;/span>&lt;span class="lnt"> 694
&lt;/span>&lt;span class="lnt"> 695
&lt;/span>&lt;span class="lnt"> 696
&lt;/span>&lt;span class="lnt"> 697
&lt;/span>&lt;span class="lnt"> 698
&lt;/span>&lt;span class="lnt"> 699
&lt;/span>&lt;span class="lnt"> 700
&lt;/span>&lt;span class="lnt"> 701
&lt;/span>&lt;span class="lnt"> 702
&lt;/span>&lt;span class="lnt"> 703
&lt;/span>&lt;span class="lnt"> 704
&lt;/span>&lt;span class="lnt"> 705
&lt;/span>&lt;span class="lnt"> 706
&lt;/span>&lt;span class="lnt"> 707
&lt;/span>&lt;span class="lnt"> 708
&lt;/span>&lt;span class="lnt"> 709
&lt;/span>&lt;span class="lnt"> 710
&lt;/span>&lt;span class="lnt"> 711
&lt;/span>&lt;span class="lnt"> 712
&lt;/span>&lt;span class="lnt"> 713
&lt;/span>&lt;span class="lnt"> 714
&lt;/span>&lt;span class="lnt"> 715
&lt;/span>&lt;span class="lnt"> 716
&lt;/span>&lt;span class="lnt"> 717
&lt;/span>&lt;span class="lnt"> 718
&lt;/span>&lt;span class="lnt"> 719
&lt;/span>&lt;span class="lnt"> 720
&lt;/span>&lt;span class="lnt"> 721
&lt;/span>&lt;span class="lnt"> 722
&lt;/span>&lt;span class="lnt"> 723
&lt;/span>&lt;span class="lnt"> 724
&lt;/span>&lt;span class="lnt"> 725
&lt;/span>&lt;span class="lnt"> 726
&lt;/span>&lt;span class="lnt"> 727
&lt;/span>&lt;span class="lnt"> 728
&lt;/span>&lt;span class="lnt"> 729
&lt;/span>&lt;span class="lnt"> 730
&lt;/span>&lt;span class="lnt"> 731
&lt;/span>&lt;span class="lnt"> 732
&lt;/span>&lt;span class="lnt"> 733
&lt;/span>&lt;span class="lnt"> 734
&lt;/span>&lt;span class="lnt"> 735
&lt;/span>&lt;span class="lnt"> 736
&lt;/span>&lt;span class="lnt"> 737
&lt;/span>&lt;span class="lnt"> 738
&lt;/span>&lt;span class="lnt"> 739
&lt;/span>&lt;span class="lnt"> 740
&lt;/span>&lt;span class="lnt"> 741
&lt;/span>&lt;span class="lnt"> 742
&lt;/span>&lt;span class="lnt"> 743
&lt;/span>&lt;span class="lnt"> 744
&lt;/span>&lt;span class="lnt"> 745
&lt;/span>&lt;span class="lnt"> 746
&lt;/span>&lt;span class="lnt"> 747
&lt;/span>&lt;span class="lnt"> 748
&lt;/span>&lt;span class="lnt"> 749
&lt;/span>&lt;span class="lnt"> 750
&lt;/span>&lt;span class="lnt"> 751
&lt;/span>&lt;span class="lnt"> 752
&lt;/span>&lt;span class="lnt"> 753
&lt;/span>&lt;span class="lnt"> 754
&lt;/span>&lt;span class="lnt"> 755
&lt;/span>&lt;span class="lnt"> 756
&lt;/span>&lt;span class="lnt"> 757
&lt;/span>&lt;span class="lnt"> 758
&lt;/span>&lt;span class="lnt"> 759
&lt;/span>&lt;span class="lnt"> 760
&lt;/span>&lt;span class="lnt"> 761
&lt;/span>&lt;span class="lnt"> 762
&lt;/span>&lt;span class="lnt"> 763
&lt;/span>&lt;span class="lnt"> 764
&lt;/span>&lt;span class="lnt"> 765
&lt;/span>&lt;span class="lnt"> 766
&lt;/span>&lt;span class="lnt"> 767
&lt;/span>&lt;span class="lnt"> 768
&lt;/span>&lt;span class="lnt"> 769
&lt;/span>&lt;span class="lnt"> 770
&lt;/span>&lt;span class="lnt"> 771
&lt;/span>&lt;span class="lnt"> 772
&lt;/span>&lt;span class="lnt"> 773
&lt;/span>&lt;span class="lnt"> 774
&lt;/span>&lt;span class="lnt"> 775
&lt;/span>&lt;span class="lnt"> 776
&lt;/span>&lt;span class="lnt"> 777
&lt;/span>&lt;span class="lnt"> 778
&lt;/span>&lt;span class="lnt"> 779
&lt;/span>&lt;span class="lnt"> 780
&lt;/span>&lt;span class="lnt"> 781
&lt;/span>&lt;span class="lnt"> 782
&lt;/span>&lt;span class="lnt"> 783
&lt;/span>&lt;span class="lnt"> 784
&lt;/span>&lt;span class="lnt"> 785
&lt;/span>&lt;span class="lnt"> 786
&lt;/span>&lt;span class="lnt"> 787
&lt;/span>&lt;span class="lnt"> 788
&lt;/span>&lt;span class="lnt"> 789
&lt;/span>&lt;span class="lnt"> 790
&lt;/span>&lt;span class="lnt"> 791
&lt;/span>&lt;span class="lnt"> 792
&lt;/span>&lt;span class="lnt"> 793
&lt;/span>&lt;span class="lnt"> 794
&lt;/span>&lt;span class="lnt"> 795
&lt;/span>&lt;span class="lnt"> 796
&lt;/span>&lt;span class="lnt"> 797
&lt;/span>&lt;span class="lnt"> 798
&lt;/span>&lt;span class="lnt"> 799
&lt;/span>&lt;span class="lnt"> 800
&lt;/span>&lt;span class="lnt"> 801
&lt;/span>&lt;span class="lnt"> 802
&lt;/span>&lt;span class="lnt"> 803
&lt;/span>&lt;span class="lnt"> 804
&lt;/span>&lt;span class="lnt"> 805
&lt;/span>&lt;span class="lnt"> 806
&lt;/span>&lt;span class="lnt"> 807
&lt;/span>&lt;span class="lnt"> 808
&lt;/span>&lt;span class="lnt"> 809
&lt;/span>&lt;span class="lnt"> 810
&lt;/span>&lt;span class="lnt"> 811
&lt;/span>&lt;span class="lnt"> 812
&lt;/span>&lt;span class="lnt"> 813
&lt;/span>&lt;span class="lnt"> 814
&lt;/span>&lt;span class="lnt"> 815
&lt;/span>&lt;span class="lnt"> 816
&lt;/span>&lt;span class="lnt"> 817
&lt;/span>&lt;span class="lnt"> 818
&lt;/span>&lt;span class="lnt"> 819
&lt;/span>&lt;span class="lnt"> 820
&lt;/span>&lt;span class="lnt"> 821
&lt;/span>&lt;span class="lnt"> 822
&lt;/span>&lt;span class="lnt"> 823
&lt;/span>&lt;span class="lnt"> 824
&lt;/span>&lt;span class="lnt"> 825
&lt;/span>&lt;span class="lnt"> 826
&lt;/span>&lt;span class="lnt"> 827
&lt;/span>&lt;span class="lnt"> 828
&lt;/span>&lt;span class="lnt"> 829
&lt;/span>&lt;span class="lnt"> 830
&lt;/span>&lt;span class="lnt"> 831
&lt;/span>&lt;span class="lnt"> 832
&lt;/span>&lt;span class="lnt"> 833
&lt;/span>&lt;span class="lnt"> 834
&lt;/span>&lt;span class="lnt"> 835
&lt;/span>&lt;span class="lnt"> 836
&lt;/span>&lt;span class="lnt"> 837
&lt;/span>&lt;span class="lnt"> 838
&lt;/span>&lt;span class="lnt"> 839
&lt;/span>&lt;span class="lnt"> 840
&lt;/span>&lt;span class="lnt"> 841
&lt;/span>&lt;span class="lnt"> 842
&lt;/span>&lt;span class="lnt"> 843
&lt;/span>&lt;span class="lnt"> 844
&lt;/span>&lt;span class="lnt"> 845
&lt;/span>&lt;span class="lnt"> 846
&lt;/span>&lt;span class="lnt"> 847
&lt;/span>&lt;span class="lnt"> 848
&lt;/span>&lt;span class="lnt"> 849
&lt;/span>&lt;span class="lnt"> 850
&lt;/span>&lt;span class="lnt"> 851
&lt;/span>&lt;span class="lnt"> 852
&lt;/span>&lt;span class="lnt"> 853
&lt;/span>&lt;span class="lnt"> 854
&lt;/span>&lt;span class="lnt"> 855
&lt;/span>&lt;span class="lnt"> 856
&lt;/span>&lt;span class="lnt"> 857
&lt;/span>&lt;span class="lnt"> 858
&lt;/span>&lt;span class="lnt"> 859
&lt;/span>&lt;span class="lnt"> 860
&lt;/span>&lt;span class="lnt"> 861
&lt;/span>&lt;span class="lnt"> 862
&lt;/span>&lt;span class="lnt"> 863
&lt;/span>&lt;span class="lnt"> 864
&lt;/span>&lt;span class="lnt"> 865
&lt;/span>&lt;span class="lnt"> 866
&lt;/span>&lt;span class="lnt"> 867
&lt;/span>&lt;span class="lnt"> 868
&lt;/span>&lt;span class="lnt"> 869
&lt;/span>&lt;span class="lnt"> 870
&lt;/span>&lt;span class="lnt"> 871
&lt;/span>&lt;span class="lnt"> 872
&lt;/span>&lt;span class="lnt"> 873
&lt;/span>&lt;span class="lnt"> 874
&lt;/span>&lt;span class="lnt"> 875
&lt;/span>&lt;span class="lnt"> 876
&lt;/span>&lt;span class="lnt"> 877
&lt;/span>&lt;span class="lnt"> 878
&lt;/span>&lt;span class="lnt"> 879
&lt;/span>&lt;span class="lnt"> 880
&lt;/span>&lt;span class="lnt"> 881
&lt;/span>&lt;span class="lnt"> 882
&lt;/span>&lt;span class="lnt"> 883
&lt;/span>&lt;span class="lnt"> 884
&lt;/span>&lt;span class="lnt"> 885
&lt;/span>&lt;span class="lnt"> 886
&lt;/span>&lt;span class="lnt"> 887
&lt;/span>&lt;span class="lnt"> 888
&lt;/span>&lt;span class="lnt"> 889
&lt;/span>&lt;span class="lnt"> 890
&lt;/span>&lt;span class="lnt"> 891
&lt;/span>&lt;span class="lnt"> 892
&lt;/span>&lt;span class="lnt"> 893
&lt;/span>&lt;span class="lnt"> 894
&lt;/span>&lt;span class="lnt"> 895
&lt;/span>&lt;span class="lnt"> 896
&lt;/span>&lt;span class="lnt"> 897
&lt;/span>&lt;span class="lnt"> 898
&lt;/span>&lt;span class="lnt"> 899
&lt;/span>&lt;span class="lnt"> 900
&lt;/span>&lt;span class="lnt"> 901
&lt;/span>&lt;span class="lnt"> 902
&lt;/span>&lt;span class="lnt"> 903
&lt;/span>&lt;span class="lnt"> 904
&lt;/span>&lt;span class="lnt"> 905
&lt;/span>&lt;span class="lnt"> 906
&lt;/span>&lt;span class="lnt"> 907
&lt;/span>&lt;span class="lnt"> 908
&lt;/span>&lt;span class="lnt"> 909
&lt;/span>&lt;span class="lnt"> 910
&lt;/span>&lt;span class="lnt"> 911
&lt;/span>&lt;span class="lnt"> 912
&lt;/span>&lt;span class="lnt"> 913
&lt;/span>&lt;span class="lnt"> 914
&lt;/span>&lt;span class="lnt"> 915
&lt;/span>&lt;span class="lnt"> 916
&lt;/span>&lt;span class="lnt"> 917
&lt;/span>&lt;span class="lnt"> 918
&lt;/span>&lt;span class="lnt"> 919
&lt;/span>&lt;span class="lnt"> 920
&lt;/span>&lt;span class="lnt"> 921
&lt;/span>&lt;span class="lnt"> 922
&lt;/span>&lt;span class="lnt"> 923
&lt;/span>&lt;span class="lnt"> 924
&lt;/span>&lt;span class="lnt"> 925
&lt;/span>&lt;span class="lnt"> 926
&lt;/span>&lt;span class="lnt"> 927
&lt;/span>&lt;span class="lnt"> 928
&lt;/span>&lt;span class="lnt"> 929
&lt;/span>&lt;span class="lnt"> 930
&lt;/span>&lt;span class="lnt"> 931
&lt;/span>&lt;span class="lnt"> 932
&lt;/span>&lt;span class="lnt"> 933
&lt;/span>&lt;span class="lnt"> 934
&lt;/span>&lt;span class="lnt"> 935
&lt;/span>&lt;span class="lnt"> 936
&lt;/span>&lt;span class="lnt"> 937
&lt;/span>&lt;span class="lnt"> 938
&lt;/span>&lt;span class="lnt"> 939
&lt;/span>&lt;span class="lnt"> 940
&lt;/span>&lt;span class="lnt"> 941
&lt;/span>&lt;span class="lnt"> 942
&lt;/span>&lt;span class="lnt"> 943
&lt;/span>&lt;span class="lnt"> 944
&lt;/span>&lt;span class="lnt"> 945
&lt;/span>&lt;span class="lnt"> 946
&lt;/span>&lt;span class="lnt"> 947
&lt;/span>&lt;span class="lnt"> 948
&lt;/span>&lt;span class="lnt"> 949
&lt;/span>&lt;span class="lnt"> 950
&lt;/span>&lt;span class="lnt"> 951
&lt;/span>&lt;span class="lnt"> 952
&lt;/span>&lt;span class="lnt"> 953
&lt;/span>&lt;span class="lnt"> 954
&lt;/span>&lt;span class="lnt"> 955
&lt;/span>&lt;span class="lnt"> 956
&lt;/span>&lt;span class="lnt"> 957
&lt;/span>&lt;span class="lnt"> 958
&lt;/span>&lt;span class="lnt"> 959
&lt;/span>&lt;span class="lnt"> 960
&lt;/span>&lt;span class="lnt"> 961
&lt;/span>&lt;span class="lnt"> 962
&lt;/span>&lt;span class="lnt"> 963
&lt;/span>&lt;span class="lnt"> 964
&lt;/span>&lt;span class="lnt"> 965
&lt;/span>&lt;span class="lnt"> 966
&lt;/span>&lt;span class="lnt"> 967
&lt;/span>&lt;span class="lnt"> 968
&lt;/span>&lt;span class="lnt"> 969
&lt;/span>&lt;span class="lnt"> 970
&lt;/span>&lt;span class="lnt"> 971
&lt;/span>&lt;span class="lnt"> 972
&lt;/span>&lt;span class="lnt"> 973
&lt;/span>&lt;span class="lnt"> 974
&lt;/span>&lt;span class="lnt"> 975
&lt;/span>&lt;span class="lnt"> 976
&lt;/span>&lt;span class="lnt"> 977
&lt;/span>&lt;span class="lnt"> 978
&lt;/span>&lt;span class="lnt"> 979
&lt;/span>&lt;span class="lnt"> 980
&lt;/span>&lt;span class="lnt"> 981
&lt;/span>&lt;span class="lnt"> 982
&lt;/span>&lt;span class="lnt"> 983
&lt;/span>&lt;span class="lnt"> 984
&lt;/span>&lt;span class="lnt"> 985
&lt;/span>&lt;span class="lnt"> 986
&lt;/span>&lt;span class="lnt"> 987
&lt;/span>&lt;span class="lnt"> 988
&lt;/span>&lt;span class="lnt"> 989
&lt;/span>&lt;span class="lnt"> 990
&lt;/span>&lt;span class="lnt"> 991
&lt;/span>&lt;span class="lnt"> 992
&lt;/span>&lt;span class="lnt"> 993
&lt;/span>&lt;span class="lnt"> 994
&lt;/span>&lt;span class="lnt"> 995
&lt;/span>&lt;span class="lnt"> 996
&lt;/span>&lt;span class="lnt"> 997
&lt;/span>&lt;span class="lnt"> 998
&lt;/span>&lt;span class="lnt"> 999
&lt;/span>&lt;span class="lnt">1000
&lt;/span>&lt;span class="lnt">1001
&lt;/span>&lt;span class="lnt">1002
&lt;/span>&lt;span class="lnt">1003
&lt;/span>&lt;span class="lnt">1004
&lt;/span>&lt;span class="lnt">1005
&lt;/span>&lt;span class="lnt">1006
&lt;/span>&lt;span class="lnt">1007
&lt;/span>&lt;span class="lnt">1008
&lt;/span>&lt;span class="lnt">1009
&lt;/span>&lt;span class="lnt">1010
&lt;/span>&lt;span class="lnt">1011
&lt;/span>&lt;span class="lnt">1012
&lt;/span>&lt;span class="lnt">1013
&lt;/span>&lt;span class="lnt">1014
&lt;/span>&lt;span class="lnt">1015
&lt;/span>&lt;span class="lnt">1016
&lt;/span>&lt;span class="lnt">1017
&lt;/span>&lt;span class="lnt">1018
&lt;/span>&lt;span class="lnt">1019
&lt;/span>&lt;span class="lnt">1020
&lt;/span>&lt;span class="lnt">1021
&lt;/span>&lt;span class="lnt">1022
&lt;/span>&lt;span class="lnt">1023
&lt;/span>&lt;span class="lnt">1024
&lt;/span>&lt;span class="lnt">1025
&lt;/span>&lt;span class="lnt">1026
&lt;/span>&lt;span class="lnt">1027
&lt;/span>&lt;span class="lnt">1028
&lt;/span>&lt;span class="lnt">1029
&lt;/span>&lt;span class="lnt">1030
&lt;/span>&lt;span class="lnt">1031
&lt;/span>&lt;span class="lnt">1032
&lt;/span>&lt;span class="lnt">1033
&lt;/span>&lt;span class="lnt">1034
&lt;/span>&lt;span class="lnt">1035
&lt;/span>&lt;span class="lnt">1036
&lt;/span>&lt;span class="lnt">1037
&lt;/span>&lt;span class="lnt">1038
&lt;/span>&lt;span class="lnt">1039
&lt;/span>&lt;span class="lnt">1040
&lt;/span>&lt;span class="lnt">1041
&lt;/span>&lt;span class="lnt">1042
&lt;/span>&lt;span class="lnt">1043
&lt;/span>&lt;span class="lnt">1044
&lt;/span>&lt;span class="lnt">1045
&lt;/span>&lt;span class="lnt">1046
&lt;/span>&lt;span class="lnt">1047
&lt;/span>&lt;span class="lnt">1048
&lt;/span>&lt;span class="lnt">1049
&lt;/span>&lt;span class="lnt">1050
&lt;/span>&lt;span class="lnt">1051
&lt;/span>&lt;span class="lnt">1052
&lt;/span>&lt;span class="lnt">1053
&lt;/span>&lt;span class="lnt">1054
&lt;/span>&lt;span class="lnt">1055
&lt;/span>&lt;span class="lnt">1056
&lt;/span>&lt;span class="lnt">1057
&lt;/span>&lt;span class="lnt">1058
&lt;/span>&lt;span class="lnt">1059
&lt;/span>&lt;span class="lnt">1060
&lt;/span>&lt;span class="lnt">1061
&lt;/span>&lt;span class="lnt">1062
&lt;/span>&lt;span class="lnt">1063
&lt;/span>&lt;span class="lnt">1064
&lt;/span>&lt;span class="lnt">1065
&lt;/span>&lt;span class="lnt">1066
&lt;/span>&lt;span class="lnt">1067
&lt;/span>&lt;span class="lnt">1068
&lt;/span>&lt;span class="lnt">1069
&lt;/span>&lt;span class="lnt">1070
&lt;/span>&lt;span class="lnt">1071
&lt;/span>&lt;span class="lnt">1072
&lt;/span>&lt;span class="lnt">1073
&lt;/span>&lt;span class="lnt">1074
&lt;/span>&lt;span class="lnt">1075
&lt;/span>&lt;span class="lnt">1076
&lt;/span>&lt;span class="lnt">1077
&lt;/span>&lt;span class="lnt">1078
&lt;/span>&lt;span class="lnt">1079
&lt;/span>&lt;span class="lnt">1080
&lt;/span>&lt;span class="lnt">1081
&lt;/span>&lt;span class="lnt">1082
&lt;/span>&lt;span class="lnt">1083
&lt;/span>&lt;span class="lnt">1084
&lt;/span>&lt;span class="lnt">1085
&lt;/span>&lt;span class="lnt">1086
&lt;/span>&lt;span class="lnt">1087
&lt;/span>&lt;span class="lnt">1088
&lt;/span>&lt;span class="lnt">1089
&lt;/span>&lt;span class="lnt">1090
&lt;/span>&lt;span class="lnt">1091
&lt;/span>&lt;span class="lnt">1092
&lt;/span>&lt;span class="lnt">1093
&lt;/span>&lt;span class="lnt">1094
&lt;/span>&lt;span class="lnt">1095
&lt;/span>&lt;span class="lnt">1096
&lt;/span>&lt;span class="lnt">1097
&lt;/span>&lt;span class="lnt">1098
&lt;/span>&lt;span class="lnt">1099
&lt;/span>&lt;span class="lnt">1100
&lt;/span>&lt;span class="lnt">1101
&lt;/span>&lt;span class="lnt">1102
&lt;/span>&lt;span class="lnt">1103
&lt;/span>&lt;span class="lnt">1104
&lt;/span>&lt;span class="lnt">1105
&lt;/span>&lt;span class="lnt">1106
&lt;/span>&lt;span class="lnt">1107
&lt;/span>&lt;span class="lnt">1108
&lt;/span>&lt;span class="lnt">1109
&lt;/span>&lt;span class="lnt">1110
&lt;/span>&lt;span class="lnt">1111
&lt;/span>&lt;span class="lnt">1112
&lt;/span>&lt;span class="lnt">1113
&lt;/span>&lt;span class="lnt">1114
&lt;/span>&lt;span class="lnt">1115
&lt;/span>&lt;span class="lnt">1116
&lt;/span>&lt;span class="lnt">1117
&lt;/span>&lt;span class="lnt">1118
&lt;/span>&lt;span class="lnt">1119
&lt;/span>&lt;span class="lnt">1120
&lt;/span>&lt;span class="lnt">1121
&lt;/span>&lt;span class="lnt">1122
&lt;/span>&lt;span class="lnt">1123
&lt;/span>&lt;span class="lnt">1124
&lt;/span>&lt;span class="lnt">1125
&lt;/span>&lt;span class="lnt">1126
&lt;/span>&lt;span class="lnt">1127
&lt;/span>&lt;span class="lnt">1128
&lt;/span>&lt;span class="lnt">1129
&lt;/span>&lt;span class="lnt">1130
&lt;/span>&lt;span class="lnt">1131
&lt;/span>&lt;span class="lnt">1132
&lt;/span>&lt;span class="lnt">1133
&lt;/span>&lt;span class="lnt">1134
&lt;/span>&lt;span class="lnt">1135
&lt;/span>&lt;span class="lnt">1136
&lt;/span>&lt;span class="lnt">1137
&lt;/span>&lt;span class="lnt">1138
&lt;/span>&lt;span class="lnt">1139
&lt;/span>&lt;span class="lnt">1140
&lt;/span>&lt;span class="lnt">1141
&lt;/span>&lt;span class="lnt">1142
&lt;/span>&lt;span class="lnt">1143
&lt;/span>&lt;span class="lnt">1144
&lt;/span>&lt;span class="lnt">1145
&lt;/span>&lt;span class="lnt">1146
&lt;/span>&lt;span class="lnt">1147
&lt;/span>&lt;span class="lnt">1148
&lt;/span>&lt;span class="lnt">1149
&lt;/span>&lt;span class="lnt">1150
&lt;/span>&lt;span class="lnt">1151
&lt;/span>&lt;span class="lnt">1152
&lt;/span>&lt;span class="lnt">1153
&lt;/span>&lt;span class="lnt">1154
&lt;/span>&lt;span class="lnt">1155
&lt;/span>&lt;span class="lnt">1156
&lt;/span>&lt;span class="lnt">1157
&lt;/span>&lt;span class="lnt">1158
&lt;/span>&lt;span class="lnt">1159
&lt;/span>&lt;span class="lnt">1160
&lt;/span>&lt;span class="lnt">1161
&lt;/span>&lt;span class="lnt">1162
&lt;/span>&lt;span class="lnt">1163
&lt;/span>&lt;span class="lnt">1164
&lt;/span>&lt;span class="lnt">1165
&lt;/span>&lt;span class="lnt">1166
&lt;/span>&lt;span class="lnt">1167
&lt;/span>&lt;span class="lnt">1168
&lt;/span>&lt;span class="lnt">1169
&lt;/span>&lt;span class="lnt">1170
&lt;/span>&lt;span class="lnt">1171
&lt;/span>&lt;span class="lnt">1172
&lt;/span>&lt;span class="lnt">1173
&lt;/span>&lt;span class="lnt">1174
&lt;/span>&lt;span class="lnt">1175
&lt;/span>&lt;span class="lnt">1176
&lt;/span>&lt;span class="lnt">1177
&lt;/span>&lt;span class="lnt">1178
&lt;/span>&lt;span class="lnt">1179
&lt;/span>&lt;span class="lnt">1180
&lt;/span>&lt;span class="lnt">1181
&lt;/span>&lt;span class="lnt">1182
&lt;/span>&lt;span class="lnt">1183
&lt;/span>&lt;span class="lnt">1184
&lt;/span>&lt;span class="lnt">1185
&lt;/span>&lt;span class="lnt">1186
&lt;/span>&lt;span class="lnt">1187
&lt;/span>&lt;span class="lnt">1188
&lt;/span>&lt;span class="lnt">1189
&lt;/span>&lt;span class="lnt">1190
&lt;/span>&lt;span class="lnt">1191
&lt;/span>&lt;span class="lnt">1192
&lt;/span>&lt;span class="lnt">1193
&lt;/span>&lt;span class="lnt">1194
&lt;/span>&lt;span class="lnt">1195
&lt;/span>&lt;span class="lnt">1196
&lt;/span>&lt;span class="lnt">1197
&lt;/span>&lt;span class="lnt">1198
&lt;/span>&lt;span class="lnt">1199
&lt;/span>&lt;span class="lnt">1200
&lt;/span>&lt;span class="lnt">1201
&lt;/span>&lt;span class="lnt">1202
&lt;/span>&lt;span class="lnt">1203
&lt;/span>&lt;span class="lnt">1204
&lt;/span>&lt;span class="lnt">1205
&lt;/span>&lt;span class="lnt">1206
&lt;/span>&lt;span class="lnt">1207
&lt;/span>&lt;span class="lnt">1208
&lt;/span>&lt;span class="lnt">1209
&lt;/span>&lt;span class="lnt">1210
&lt;/span>&lt;span class="lnt">1211
&lt;/span>&lt;span class="lnt">1212
&lt;/span>&lt;span class="lnt">1213
&lt;/span>&lt;span class="lnt">1214
&lt;/span>&lt;span class="lnt">1215
&lt;/span>&lt;span class="lnt">1216
&lt;/span>&lt;span class="lnt">1217
&lt;/span>&lt;span class="lnt">1218
&lt;/span>&lt;span class="lnt">1219
&lt;/span>&lt;span class="lnt">1220
&lt;/span>&lt;span class="lnt">1221
&lt;/span>&lt;span class="lnt">1222
&lt;/span>&lt;span class="lnt">1223
&lt;/span>&lt;span class="lnt">1224
&lt;/span>&lt;span class="lnt">1225
&lt;/span>&lt;span class="lnt">1226
&lt;/span>&lt;span class="lnt">1227
&lt;/span>&lt;span class="lnt">1228
&lt;/span>&lt;span class="lnt">1229
&lt;/span>&lt;span class="lnt">1230
&lt;/span>&lt;span class="lnt">1231
&lt;/span>&lt;span class="lnt">1232
&lt;/span>&lt;span class="lnt">1233
&lt;/span>&lt;span class="lnt">1234
&lt;/span>&lt;span class="lnt">1235
&lt;/span>&lt;span class="lnt">1236
&lt;/span>&lt;span class="lnt">1237
&lt;/span>&lt;span class="lnt">1238
&lt;/span>&lt;span class="lnt">1239
&lt;/span>&lt;span class="lnt">1240
&lt;/span>&lt;span class="lnt">1241
&lt;/span>&lt;span class="lnt">1242
&lt;/span>&lt;span class="lnt">1243
&lt;/span>&lt;span class="lnt">1244
&lt;/span>&lt;span class="lnt">1245
&lt;/span>&lt;span class="lnt">1246
&lt;/span>&lt;span class="lnt">1247
&lt;/span>&lt;span class="lnt">1248
&lt;/span>&lt;span class="lnt">1249
&lt;/span>&lt;span class="lnt">1250
&lt;/span>&lt;span class="lnt">1251
&lt;/span>&lt;span class="lnt">1252
&lt;/span>&lt;span class="lnt">1253
&lt;/span>&lt;span class="lnt">1254
&lt;/span>&lt;span class="lnt">1255
&lt;/span>&lt;span class="lnt">1256
&lt;/span>&lt;span class="lnt">1257
&lt;/span>&lt;span class="lnt">1258
&lt;/span>&lt;span class="lnt">1259
&lt;/span>&lt;span class="lnt">1260
&lt;/span>&lt;span class="lnt">1261
&lt;/span>&lt;span class="lnt">1262
&lt;/span>&lt;span class="lnt">1263
&lt;/span>&lt;span class="lnt">1264
&lt;/span>&lt;span class="lnt">1265
&lt;/span>&lt;span class="lnt">1266
&lt;/span>&lt;span class="lnt">1267
&lt;/span>&lt;span class="lnt">1268
&lt;/span>&lt;span class="lnt">1269
&lt;/span>&lt;span class="lnt">1270
&lt;/span>&lt;span class="lnt">1271
&lt;/span>&lt;span class="lnt">1272
&lt;/span>&lt;span class="lnt">1273
&lt;/span>&lt;span class="lnt">1274
&lt;/span>&lt;span class="lnt">1275
&lt;/span>&lt;span class="lnt">1276
&lt;/span>&lt;span class="lnt">1277
&lt;/span>&lt;span class="lnt">1278
&lt;/span>&lt;span class="lnt">1279
&lt;/span>&lt;span class="lnt">1280
&lt;/span>&lt;span class="lnt">1281
&lt;/span>&lt;span class="lnt">1282
&lt;/span>&lt;span class="lnt">1283
&lt;/span>&lt;span class="lnt">1284
&lt;/span>&lt;span class="lnt">1285
&lt;/span>&lt;span class="lnt">1286
&lt;/span>&lt;span class="lnt">1287
&lt;/span>&lt;span class="lnt">1288
&lt;/span>&lt;span class="lnt">1289
&lt;/span>&lt;span class="lnt">1290
&lt;/span>&lt;span class="lnt">1291
&lt;/span>&lt;span class="lnt">1292
&lt;/span>&lt;span class="lnt">1293
&lt;/span>&lt;span class="lnt">1294
&lt;/span>&lt;span class="lnt">1295
&lt;/span>&lt;span class="lnt">1296
&lt;/span>&lt;span class="lnt">1297
&lt;/span>&lt;span class="lnt">1298
&lt;/span>&lt;span class="lnt">1299
&lt;/span>&lt;span class="lnt">1300
&lt;/span>&lt;span class="lnt">1301
&lt;/span>&lt;span class="lnt">1302
&lt;/span>&lt;span class="lnt">1303
&lt;/span>&lt;span class="lnt">1304
&lt;/span>&lt;span class="lnt">1305
&lt;/span>&lt;span class="lnt">1306
&lt;/span>&lt;span class="lnt">1307
&lt;/span>&lt;span class="lnt">1308
&lt;/span>&lt;span class="lnt">1309
&lt;/span>&lt;span class="lnt">1310
&lt;/span>&lt;span class="lnt">1311
&lt;/span>&lt;span class="lnt">1312
&lt;/span>&lt;span class="lnt">1313
&lt;/span>&lt;span class="lnt">1314
&lt;/span>&lt;span class="lnt">1315
&lt;/span>&lt;span class="lnt">1316
&lt;/span>&lt;span class="lnt">1317
&lt;/span>&lt;span class="lnt">1318
&lt;/span>&lt;span class="lnt">1319
&lt;/span>&lt;span class="lnt">1320
&lt;/span>&lt;span class="lnt">1321
&lt;/span>&lt;span class="lnt">1322
&lt;/span>&lt;span class="lnt">1323
&lt;/span>&lt;span class="lnt">1324
&lt;/span>&lt;span class="lnt">1325
&lt;/span>&lt;span class="lnt">1326
&lt;/span>&lt;span class="lnt">1327
&lt;/span>&lt;span class="lnt">1328
&lt;/span>&lt;span class="lnt">1329
&lt;/span>&lt;span class="lnt">1330
&lt;/span>&lt;span class="lnt">1331
&lt;/span>&lt;span class="lnt">1332
&lt;/span>&lt;span class="lnt">1333
&lt;/span>&lt;span class="lnt">1334
&lt;/span>&lt;span class="lnt">1335
&lt;/span>&lt;span class="lnt">1336
&lt;/span>&lt;span class="lnt">1337
&lt;/span>&lt;span class="lnt">1338
&lt;/span>&lt;span class="lnt">1339
&lt;/span>&lt;span class="lnt">1340
&lt;/span>&lt;span class="lnt">1341
&lt;/span>&lt;span class="lnt">1342
&lt;/span>&lt;span class="lnt">1343
&lt;/span>&lt;span class="lnt">1344
&lt;/span>&lt;span class="lnt">1345
&lt;/span>&lt;span class="lnt">1346
&lt;/span>&lt;span class="lnt">1347
&lt;/span>&lt;span class="lnt">1348
&lt;/span>&lt;span class="lnt">1349
&lt;/span>&lt;span class="lnt">1350
&lt;/span>&lt;span class="lnt">1351
&lt;/span>&lt;span class="lnt">1352
&lt;/span>&lt;span class="lnt">1353
&lt;/span>&lt;span class="lnt">1354
&lt;/span>&lt;span class="lnt">1355
&lt;/span>&lt;span class="lnt">1356
&lt;/span>&lt;span class="lnt">1357
&lt;/span>&lt;span class="lnt">1358
&lt;/span>&lt;span class="lnt">1359
&lt;/span>&lt;span class="lnt">1360
&lt;/span>&lt;span class="lnt">1361
&lt;/span>&lt;span class="lnt">1362
&lt;/span>&lt;span class="lnt">1363
&lt;/span>&lt;span class="lnt">1364
&lt;/span>&lt;span class="lnt">1365
&lt;/span>&lt;span class="lnt">1366
&lt;/span>&lt;span class="lnt">1367
&lt;/span>&lt;span class="lnt">1368
&lt;/span>&lt;span class="lnt">1369
&lt;/span>&lt;span class="lnt">1370
&lt;/span>&lt;span class="lnt">1371
&lt;/span>&lt;span class="lnt">1372
&lt;/span>&lt;span class="lnt">1373
&lt;/span>&lt;span class="lnt">1374
&lt;/span>&lt;span class="lnt">1375
&lt;/span>&lt;span class="lnt">1376
&lt;/span>&lt;span class="lnt">1377
&lt;/span>&lt;span class="lnt">1378
&lt;/span>&lt;span class="lnt">1379
&lt;/span>&lt;span class="lnt">1380
&lt;/span>&lt;span class="lnt">1381
&lt;/span>&lt;span class="lnt">1382
&lt;/span>&lt;span class="lnt">1383
&lt;/span>&lt;span class="lnt">1384
&lt;/span>&lt;span class="lnt">1385
&lt;/span>&lt;span class="lnt">1386
&lt;/span>&lt;span class="lnt">1387
&lt;/span>&lt;span class="lnt">1388
&lt;/span>&lt;span class="lnt">1389
&lt;/span>&lt;span class="lnt">1390
&lt;/span>&lt;span class="lnt">1391
&lt;/span>&lt;span class="lnt">1392
&lt;/span>&lt;span class="lnt">1393
&lt;/span>&lt;span class="lnt">1394
&lt;/span>&lt;span class="lnt">1395
&lt;/span>&lt;span class="lnt">1396
&lt;/span>&lt;span class="lnt">1397
&lt;/span>&lt;span class="lnt">1398
&lt;/span>&lt;span class="lnt">1399
&lt;/span>&lt;span class="lnt">1400
&lt;/span>&lt;span class="lnt">1401
&lt;/span>&lt;span class="lnt">1402
&lt;/span>&lt;span class="lnt">1403
&lt;/span>&lt;span class="lnt">1404
&lt;/span>&lt;span class="lnt">1405
&lt;/span>&lt;span class="lnt">1406
&lt;/span>&lt;span class="lnt">1407
&lt;/span>&lt;span class="lnt">1408
&lt;/span>&lt;span class="lnt">1409
&lt;/span>&lt;span class="lnt">1410
&lt;/span>&lt;span class="lnt">1411
&lt;/span>&lt;span class="lnt">1412
&lt;/span>&lt;span class="lnt">1413
&lt;/span>&lt;span class="lnt">1414
&lt;/span>&lt;span class="lnt">1415
&lt;/span>&lt;span class="lnt">1416
&lt;/span>&lt;span class="lnt">1417
&lt;/span>&lt;span class="lnt">1418
&lt;/span>&lt;span class="lnt">1419
&lt;/span>&lt;span class="lnt">1420
&lt;/span>&lt;span class="lnt">1421
&lt;/span>&lt;span class="lnt">1422
&lt;/span>&lt;span class="lnt">1423
&lt;/span>&lt;span class="lnt">1424
&lt;/span>&lt;span class="lnt">1425
&lt;/span>&lt;span class="lnt">1426
&lt;/span>&lt;span class="lnt">1427
&lt;/span>&lt;span class="lnt">1428
&lt;/span>&lt;span class="lnt">1429
&lt;/span>&lt;span class="lnt">1430
&lt;/span>&lt;span class="lnt">1431
&lt;/span>&lt;span class="lnt">1432
&lt;/span>&lt;span class="lnt">1433
&lt;/span>&lt;span class="lnt">1434
&lt;/span>&lt;span class="lnt">1435
&lt;/span>&lt;span class="lnt">1436
&lt;/span>&lt;span class="lnt">1437
&lt;/span>&lt;span class="lnt">1438
&lt;/span>&lt;span class="lnt">1439
&lt;/span>&lt;span class="lnt">1440
&lt;/span>&lt;span class="lnt">1441
&lt;/span>&lt;span class="lnt">1442
&lt;/span>&lt;span class="lnt">1443
&lt;/span>&lt;span class="lnt">1444
&lt;/span>&lt;span class="lnt">1445
&lt;/span>&lt;span class="lnt">1446
&lt;/span>&lt;span class="lnt">1447
&lt;/span>&lt;span class="lnt">1448
&lt;/span>&lt;span class="lnt">1449
&lt;/span>&lt;span class="lnt">1450
&lt;/span>&lt;span class="lnt">1451
&lt;/span>&lt;span class="lnt">1452
&lt;/span>&lt;span class="lnt">1453
&lt;/span>&lt;span class="lnt">1454
&lt;/span>&lt;span class="lnt">1455
&lt;/span>&lt;span class="lnt">1456
&lt;/span>&lt;span class="lnt">1457
&lt;/span>&lt;span class="lnt">1458
&lt;/span>&lt;span class="lnt">1459
&lt;/span>&lt;span class="lnt">1460
&lt;/span>&lt;span class="lnt">1461
&lt;/span>&lt;span class="lnt">1462
&lt;/span>&lt;span class="lnt">1463
&lt;/span>&lt;span class="lnt">1464
&lt;/span>&lt;span class="lnt">1465
&lt;/span>&lt;span class="lnt">1466
&lt;/span>&lt;span class="lnt">1467
&lt;/span>&lt;span class="lnt">1468
&lt;/span>&lt;span class="lnt">1469
&lt;/span>&lt;span class="lnt">1470
&lt;/span>&lt;span class="lnt">1471
&lt;/span>&lt;span class="lnt">1472
&lt;/span>&lt;span class="lnt">1473
&lt;/span>&lt;span class="lnt">1474
&lt;/span>&lt;span class="lnt">1475
&lt;/span>&lt;span class="lnt">1476
&lt;/span>&lt;span class="lnt">1477
&lt;/span>&lt;span class="lnt">1478
&lt;/span>&lt;span class="lnt">1479
&lt;/span>&lt;span class="lnt">1480
&lt;/span>&lt;span class="lnt">1481
&lt;/span>&lt;span class="lnt">1482
&lt;/span>&lt;span class="lnt">1483
&lt;/span>&lt;span class="lnt">1484
&lt;/span>&lt;span class="lnt">1485
&lt;/span>&lt;span class="lnt">1486
&lt;/span>&lt;span class="lnt">1487
&lt;/span>&lt;span class="lnt">1488
&lt;/span>&lt;span class="lnt">1489
&lt;/span>&lt;span class="lnt">1490
&lt;/span>&lt;span class="lnt">1491
&lt;/span>&lt;span class="lnt">1492
&lt;/span>&lt;span class="lnt">1493
&lt;/span>&lt;span class="lnt">1494
&lt;/span>&lt;span class="lnt">1495
&lt;/span>&lt;span class="lnt">1496
&lt;/span>&lt;span class="lnt">1497
&lt;/span>&lt;span class="lnt">1498
&lt;/span>&lt;span class="lnt">1499
&lt;/span>&lt;span class="lnt">1500
&lt;/span>&lt;span class="lnt">1501
&lt;/span>&lt;span class="lnt">1502
&lt;/span>&lt;span class="lnt">1503
&lt;/span>&lt;span class="lnt">1504
&lt;/span>&lt;span class="lnt">1505
&lt;/span>&lt;span class="lnt">1506
&lt;/span>&lt;span class="lnt">1507
&lt;/span>&lt;span class="lnt">1508
&lt;/span>&lt;span class="lnt">1509
&lt;/span>&lt;span class="lnt">1510
&lt;/span>&lt;span class="lnt">1511
&lt;/span>&lt;span class="lnt">1512
&lt;/span>&lt;span class="lnt">1513
&lt;/span>&lt;span class="lnt">1514
&lt;/span>&lt;span class="lnt">1515
&lt;/span>&lt;span class="lnt">1516
&lt;/span>&lt;span class="lnt">1517
&lt;/span>&lt;span class="lnt">1518
&lt;/span>&lt;span class="lnt">1519
&lt;/span>&lt;span class="lnt">1520
&lt;/span>&lt;span class="lnt">1521
&lt;/span>&lt;span class="lnt">1522
&lt;/span>&lt;span class="lnt">1523
&lt;/span>&lt;span class="lnt">1524
&lt;/span>&lt;span class="lnt">1525
&lt;/span>&lt;span class="lnt">1526
&lt;/span>&lt;span class="lnt">1527
&lt;/span>&lt;span class="lnt">1528
&lt;/span>&lt;span class="lnt">1529
&lt;/span>&lt;span class="lnt">1530
&lt;/span>&lt;span class="lnt">1531
&lt;/span>&lt;span class="lnt">1532
&lt;/span>&lt;span class="lnt">1533
&lt;/span>&lt;span class="lnt">1534
&lt;/span>&lt;span class="lnt">1535
&lt;/span>&lt;span class="lnt">1536
&lt;/span>&lt;span class="lnt">1537
&lt;/span>&lt;span class="lnt">1538
&lt;/span>&lt;span class="lnt">1539
&lt;/span>&lt;span class="lnt">1540
&lt;/span>&lt;span class="lnt">1541
&lt;/span>&lt;span class="lnt">1542
&lt;/span>&lt;span class="lnt">1543
&lt;/span>&lt;span class="lnt">1544
&lt;/span>&lt;span class="lnt">1545
&lt;/span>&lt;span class="lnt">1546
&lt;/span>&lt;span class="lnt">1547
&lt;/span>&lt;span class="lnt">1548
&lt;/span>&lt;span class="lnt">1549
&lt;/span>&lt;span class="lnt">1550
&lt;/span>&lt;span class="lnt">1551
&lt;/span>&lt;span class="lnt">1552
&lt;/span>&lt;span class="lnt">1553
&lt;/span>&lt;span class="lnt">1554
&lt;/span>&lt;span class="lnt">1555
&lt;/span>&lt;span class="lnt">1556
&lt;/span>&lt;span class="lnt">1557
&lt;/span>&lt;span class="lnt">1558
&lt;/span>&lt;span class="lnt">1559
&lt;/span>&lt;span class="lnt">1560
&lt;/span>&lt;span class="lnt">1561
&lt;/span>&lt;span class="lnt">1562
&lt;/span>&lt;span class="lnt">1563
&lt;/span>&lt;span class="lnt">1564
&lt;/span>&lt;span class="lnt">1565
&lt;/span>&lt;span class="lnt">1566
&lt;/span>&lt;span class="lnt">1567
&lt;/span>&lt;span class="lnt">1568
&lt;/span>&lt;span class="lnt">1569
&lt;/span>&lt;span class="lnt">1570
&lt;/span>&lt;span class="lnt">1571
&lt;/span>&lt;span class="lnt">1572
&lt;/span>&lt;span class="lnt">1573
&lt;/span>&lt;span class="lnt">1574
&lt;/span>&lt;span class="lnt">1575
&lt;/span>&lt;span class="lnt">1576
&lt;/span>&lt;span class="lnt">1577
&lt;/span>&lt;span class="lnt">1578
&lt;/span>&lt;span class="lnt">1579
&lt;/span>&lt;span class="lnt">1580
&lt;/span>&lt;span class="lnt">1581
&lt;/span>&lt;span class="lnt">1582
&lt;/span>&lt;span class="lnt">1583
&lt;/span>&lt;span class="lnt">1584
&lt;/span>&lt;span class="lnt">1585
&lt;/span>&lt;span class="lnt">1586
&lt;/span>&lt;span class="lnt">1587
&lt;/span>&lt;span class="lnt">1588
&lt;/span>&lt;span class="lnt">1589
&lt;/span>&lt;span class="lnt">1590
&lt;/span>&lt;span class="lnt">1591
&lt;/span>&lt;span class="lnt">1592
&lt;/span>&lt;span class="lnt">1593
&lt;/span>&lt;span class="lnt">1594
&lt;/span>&lt;span class="lnt">1595
&lt;/span>&lt;span class="lnt">1596
&lt;/span>&lt;span class="lnt">1597
&lt;/span>&lt;span class="lnt">1598
&lt;/span>&lt;span class="lnt">1599
&lt;/span>&lt;span class="lnt">1600
&lt;/span>&lt;span class="lnt">1601
&lt;/span>&lt;span class="lnt">1602
&lt;/span>&lt;span class="lnt">1603
&lt;/span>&lt;span class="lnt">1604
&lt;/span>&lt;span class="lnt">1605
&lt;/span>&lt;span class="lnt">1606
&lt;/span>&lt;span class="lnt">1607
&lt;/span>&lt;span class="lnt">1608
&lt;/span>&lt;span class="lnt">1609
&lt;/span>&lt;span class="lnt">1610
&lt;/span>&lt;span class="lnt">1611
&lt;/span>&lt;span class="lnt">1612
&lt;/span>&lt;span class="lnt">1613
&lt;/span>&lt;span class="lnt">1614
&lt;/span>&lt;span class="lnt">1615
&lt;/span>&lt;span class="lnt">1616
&lt;/span>&lt;span class="lnt">1617
&lt;/span>&lt;span class="lnt">1618
&lt;/span>&lt;span class="lnt">1619
&lt;/span>&lt;span class="lnt">1620
&lt;/span>&lt;span class="lnt">1621
&lt;/span>&lt;span class="lnt">1622
&lt;/span>&lt;span class="lnt">1623
&lt;/span>&lt;span class="lnt">1624
&lt;/span>&lt;span class="lnt">1625
&lt;/span>&lt;span class="lnt">1626
&lt;/span>&lt;span class="lnt">1627
&lt;/span>&lt;span class="lnt">1628
&lt;/span>&lt;span class="lnt">1629
&lt;/span>&lt;span class="lnt">1630
&lt;/span>&lt;span class="lnt">1631
&lt;/span>&lt;span class="lnt">1632
&lt;/span>&lt;span class="lnt">1633
&lt;/span>&lt;span class="lnt">1634
&lt;/span>&lt;span class="lnt">1635
&lt;/span>&lt;span class="lnt">1636
&lt;/span>&lt;span class="lnt">1637
&lt;/span>&lt;span class="lnt">1638
&lt;/span>&lt;span class="lnt">1639
&lt;/span>&lt;span class="lnt">1640
&lt;/span>&lt;span class="lnt">1641
&lt;/span>&lt;span class="lnt">1642
&lt;/span>&lt;span class="lnt">1643
&lt;/span>&lt;span class="lnt">1644
&lt;/span>&lt;span class="lnt">1645
&lt;/span>&lt;span class="lnt">1646
&lt;/span>&lt;span class="lnt">1647
&lt;/span>&lt;span class="lnt">1648
&lt;/span>&lt;span class="lnt">1649
&lt;/span>&lt;span class="lnt">1650
&lt;/span>&lt;span class="lnt">1651
&lt;/span>&lt;span class="lnt">1652
&lt;/span>&lt;span class="lnt">1653
&lt;/span>&lt;span class="lnt">1654
&lt;/span>&lt;span class="lnt">1655
&lt;/span>&lt;span class="lnt">1656
&lt;/span>&lt;span class="lnt">1657
&lt;/span>&lt;span class="lnt">1658
&lt;/span>&lt;span class="lnt">1659
&lt;/span>&lt;span class="lnt">1660
&lt;/span>&lt;span class="lnt">1661
&lt;/span>&lt;span class="lnt">1662
&lt;/span>&lt;span class="lnt">1663
&lt;/span>&lt;span class="lnt">1664
&lt;/span>&lt;span class="lnt">1665
&lt;/span>&lt;span class="lnt">1666
&lt;/span>&lt;span class="lnt">1667
&lt;/span>&lt;span class="lnt">1668
&lt;/span>&lt;span class="lnt">1669
&lt;/span>&lt;span class="lnt">1670
&lt;/span>&lt;span class="lnt">1671
&lt;/span>&lt;span class="lnt">1672
&lt;/span>&lt;span class="lnt">1673
&lt;/span>&lt;span class="lnt">1674
&lt;/span>&lt;span class="lnt">1675
&lt;/span>&lt;span class="lnt">1676
&lt;/span>&lt;span class="lnt">1677
&lt;/span>&lt;span class="lnt">1678
&lt;/span>&lt;span class="lnt">1679
&lt;/span>&lt;span class="lnt">1680
&lt;/span>&lt;span class="lnt">1681
&lt;/span>&lt;span class="lnt">1682
&lt;/span>&lt;span class="lnt">1683
&lt;/span>&lt;span class="lnt">1684
&lt;/span>&lt;span class="lnt">1685
&lt;/span>&lt;span class="lnt">1686
&lt;/span>&lt;span class="lnt">1687
&lt;/span>&lt;span class="lnt">1688
&lt;/span>&lt;span class="lnt">1689
&lt;/span>&lt;span class="lnt">1690
&lt;/span>&lt;span class="lnt">1691
&lt;/span>&lt;span class="lnt">1692
&lt;/span>&lt;span class="lnt">1693
&lt;/span>&lt;span class="lnt">1694
&lt;/span>&lt;span class="lnt">1695
&lt;/span>&lt;span class="lnt">1696
&lt;/span>&lt;span class="lnt">1697
&lt;/span>&lt;span class="lnt">1698
&lt;/span>&lt;span class="lnt">1699
&lt;/span>&lt;span class="lnt">1700
&lt;/span>&lt;span class="lnt">1701
&lt;/span>&lt;span class="lnt">1702
&lt;/span>&lt;span class="lnt">1703
&lt;/span>&lt;span class="lnt">1704
&lt;/span>&lt;span class="lnt">1705
&lt;/span>&lt;span class="lnt">1706
&lt;/span>&lt;span class="lnt">1707
&lt;/span>&lt;span class="lnt">1708
&lt;/span>&lt;span class="lnt">1709
&lt;/span>&lt;span class="lnt">1710
&lt;/span>&lt;span class="lnt">1711
&lt;/span>&lt;span class="lnt">1712
&lt;/span>&lt;span class="lnt">1713
&lt;/span>&lt;span class="lnt">1714
&lt;/span>&lt;span class="lnt">1715
&lt;/span>&lt;span class="lnt">1716
&lt;/span>&lt;span class="lnt">1717
&lt;/span>&lt;span class="lnt">1718
&lt;/span>&lt;span class="lnt">1719
&lt;/span>&lt;span class="lnt">1720
&lt;/span>&lt;span class="lnt">1721
&lt;/span>&lt;span class="lnt">1722
&lt;/span>&lt;span class="lnt">1723
&lt;/span>&lt;span class="lnt">1724
&lt;/span>&lt;span class="lnt">1725
&lt;/span>&lt;span class="lnt">1726
&lt;/span>&lt;span class="lnt">1727
&lt;/span>&lt;span class="lnt">1728
&lt;/span>&lt;span class="lnt">1729
&lt;/span>&lt;span class="lnt">1730
&lt;/span>&lt;span class="lnt">1731
&lt;/span>&lt;span class="lnt">1732
&lt;/span>&lt;span class="lnt">1733
&lt;/span>&lt;span class="lnt">1734
&lt;/span>&lt;span class="lnt">1735
&lt;/span>&lt;span class="lnt">1736
&lt;/span>&lt;span class="lnt">1737
&lt;/span>&lt;span class="lnt">1738
&lt;/span>&lt;span class="lnt">1739
&lt;/span>&lt;span class="lnt">1740
&lt;/span>&lt;span class="lnt">1741
&lt;/span>&lt;span class="lnt">1742
&lt;/span>&lt;span class="lnt">1743
&lt;/span>&lt;span class="lnt">1744
&lt;/span>&lt;span class="lnt">1745
&lt;/span>&lt;span class="lnt">1746
&lt;/span>&lt;span class="lnt">1747
&lt;/span>&lt;span class="lnt">1748
&lt;/span>&lt;span class="lnt">1749
&lt;/span>&lt;span class="lnt">1750
&lt;/span>&lt;span class="lnt">1751
&lt;/span>&lt;span class="lnt">1752
&lt;/span>&lt;span class="lnt">1753
&lt;/span>&lt;span class="lnt">1754
&lt;/span>&lt;span class="lnt">1755
&lt;/span>&lt;span class="lnt">1756
&lt;/span>&lt;span class="lnt">1757
&lt;/span>&lt;span class="lnt">1758
&lt;/span>&lt;span class="lnt">1759
&lt;/span>&lt;span class="lnt">1760
&lt;/span>&lt;span class="lnt">1761
&lt;/span>&lt;span class="lnt">1762
&lt;/span>&lt;span class="lnt">1763
&lt;/span>&lt;span class="lnt">1764
&lt;/span>&lt;span class="lnt">1765
&lt;/span>&lt;span class="lnt">1766
&lt;/span>&lt;span class="lnt">1767
&lt;/span>&lt;span class="lnt">1768
&lt;/span>&lt;span class="lnt">1769
&lt;/span>&lt;span class="lnt">1770
&lt;/span>&lt;span class="lnt">1771
&lt;/span>&lt;span class="lnt">1772
&lt;/span>&lt;span class="lnt">1773
&lt;/span>&lt;span class="lnt">1774
&lt;/span>&lt;span class="lnt">1775
&lt;/span>&lt;span class="lnt">1776
&lt;/span>&lt;span class="lnt">1777
&lt;/span>&lt;span class="lnt">1778
&lt;/span>&lt;span class="lnt">1779
&lt;/span>&lt;span class="lnt">1780
&lt;/span>&lt;span class="lnt">1781
&lt;/span>&lt;span class="lnt">1782
&lt;/span>&lt;span class="lnt">1783
&lt;/span>&lt;span class="lnt">1784
&lt;/span>&lt;span class="lnt">1785
&lt;/span>&lt;span class="lnt">1786
&lt;/span>&lt;span class="lnt">1787
&lt;/span>&lt;span class="lnt">1788
&lt;/span>&lt;span class="lnt">1789
&lt;/span>&lt;span class="lnt">1790
&lt;/span>&lt;span class="lnt">1791
&lt;/span>&lt;span class="lnt">1792
&lt;/span>&lt;span class="lnt">1793
&lt;/span>&lt;span class="lnt">1794
&lt;/span>&lt;span class="lnt">1795
&lt;/span>&lt;span class="lnt">1796
&lt;/span>&lt;span class="lnt">1797
&lt;/span>&lt;span class="lnt">1798
&lt;/span>&lt;span class="lnt">1799
&lt;/span>&lt;span class="lnt">1800
&lt;/span>&lt;span class="lnt">1801
&lt;/span>&lt;span class="lnt">1802
&lt;/span>&lt;span class="lnt">1803
&lt;/span>&lt;span class="lnt">1804
&lt;/span>&lt;span class="lnt">1805
&lt;/span>&lt;span class="lnt">1806
&lt;/span>&lt;span class="lnt">1807
&lt;/span>&lt;span class="lnt">1808
&lt;/span>&lt;span class="lnt">1809
&lt;/span>&lt;span class="lnt">1810
&lt;/span>&lt;span class="lnt">1811
&lt;/span>&lt;span class="lnt">1812
&lt;/span>&lt;span class="lnt">1813
&lt;/span>&lt;span class="lnt">1814
&lt;/span>&lt;span class="lnt">1815
&lt;/span>&lt;span class="lnt">1816
&lt;/span>&lt;span class="lnt">1817
&lt;/span>&lt;span class="lnt">1818
&lt;/span>&lt;span class="lnt">1819
&lt;/span>&lt;span class="lnt">1820
&lt;/span>&lt;span class="lnt">1821
&lt;/span>&lt;span class="lnt">1822
&lt;/span>&lt;span class="lnt">1823
&lt;/span>&lt;span class="lnt">1824
&lt;/span>&lt;span class="lnt">1825
&lt;/span>&lt;span class="lnt">1826
&lt;/span>&lt;span class="lnt">1827
&lt;/span>&lt;span class="lnt">1828
&lt;/span>&lt;span class="lnt">1829
&lt;/span>&lt;span class="lnt">1830
&lt;/span>&lt;span class="lnt">1831
&lt;/span>&lt;span class="lnt">1832
&lt;/span>&lt;span class="lnt">1833
&lt;/span>&lt;span class="lnt">1834
&lt;/span>&lt;span class="lnt">1835
&lt;/span>&lt;span class="lnt">1836
&lt;/span>&lt;span class="lnt">1837
&lt;/span>&lt;span class="lnt">1838
&lt;/span>&lt;span class="lnt">1839
&lt;/span>&lt;span class="lnt">1840
&lt;/span>&lt;span class="lnt">1841
&lt;/span>&lt;span class="lnt">1842
&lt;/span>&lt;span class="lnt">1843
&lt;/span>&lt;span class="lnt">1844
&lt;/span>&lt;span class="lnt">1845
&lt;/span>&lt;span class="lnt">1846
&lt;/span>&lt;span class="lnt">1847
&lt;/span>&lt;span class="lnt">1848
&lt;/span>&lt;span class="lnt">1849
&lt;/span>&lt;span class="lnt">1850
&lt;/span>&lt;span class="lnt">1851
&lt;/span>&lt;span class="lnt">1852
&lt;/span>&lt;span class="lnt">1853
&lt;/span>&lt;span class="lnt">1854
&lt;/span>&lt;span class="lnt">1855
&lt;/span>&lt;span class="lnt">1856
&lt;/span>&lt;span class="lnt">1857
&lt;/span>&lt;span class="lnt">1858
&lt;/span>&lt;span class="lnt">1859
&lt;/span>&lt;span class="lnt">1860
&lt;/span>&lt;span class="lnt">1861
&lt;/span>&lt;span class="lnt">1862
&lt;/span>&lt;span class="lnt">1863
&lt;/span>&lt;span class="lnt">1864
&lt;/span>&lt;span class="lnt">1865
&lt;/span>&lt;span class="lnt">1866
&lt;/span>&lt;span class="lnt">1867
&lt;/span>&lt;span class="lnt">1868
&lt;/span>&lt;span class="lnt">1869
&lt;/span>&lt;span class="lnt">1870
&lt;/span>&lt;span class="lnt">1871
&lt;/span>&lt;span class="lnt">1872
&lt;/span>&lt;span class="lnt">1873
&lt;/span>&lt;span class="lnt">1874
&lt;/span>&lt;span class="lnt">1875
&lt;/span>&lt;span class="lnt">1876
&lt;/span>&lt;span class="lnt">1877
&lt;/span>&lt;span class="lnt">1878
&lt;/span>&lt;span class="lnt">1879
&lt;/span>&lt;span class="lnt">1880
&lt;/span>&lt;span class="lnt">1881
&lt;/span>&lt;span class="lnt">1882
&lt;/span>&lt;span class="lnt">1883
&lt;/span>&lt;span class="lnt">1884
&lt;/span>&lt;span class="lnt">1885
&lt;/span>&lt;span class="lnt">1886
&lt;/span>&lt;span class="lnt">1887
&lt;/span>&lt;span class="lnt">1888
&lt;/span>&lt;span class="lnt">1889
&lt;/span>&lt;span class="lnt">1890
&lt;/span>&lt;span class="lnt">1891
&lt;/span>&lt;span class="lnt">1892
&lt;/span>&lt;span class="lnt">1893
&lt;/span>&lt;span class="lnt">1894
&lt;/span>&lt;span class="lnt">1895
&lt;/span>&lt;span class="lnt">1896
&lt;/span>&lt;span class="lnt">1897
&lt;/span>&lt;span class="lnt">1898
&lt;/span>&lt;span class="lnt">1899
&lt;/span>&lt;span class="lnt">1900
&lt;/span>&lt;span class="lnt">1901
&lt;/span>&lt;span class="lnt">1902
&lt;/span>&lt;span class="lnt">1903
&lt;/span>&lt;span class="lnt">1904
&lt;/span>&lt;span class="lnt">1905
&lt;/span>&lt;span class="lnt">1906
&lt;/span>&lt;span class="lnt">1907
&lt;/span>&lt;span class="lnt">1908
&lt;/span>&lt;span class="lnt">1909
&lt;/span>&lt;span class="lnt">1910
&lt;/span>&lt;span class="lnt">1911
&lt;/span>&lt;span class="lnt">1912
&lt;/span>&lt;span class="lnt">1913
&lt;/span>&lt;span class="lnt">1914
&lt;/span>&lt;span class="lnt">1915
&lt;/span>&lt;span class="lnt">1916
&lt;/span>&lt;span class="lnt">1917
&lt;/span>&lt;span class="lnt">1918
&lt;/span>&lt;span class="lnt">1919
&lt;/span>&lt;span class="lnt">1920
&lt;/span>&lt;span class="lnt">1921
&lt;/span>&lt;span class="lnt">1922
&lt;/span>&lt;span class="lnt">1923
&lt;/span>&lt;span class="lnt">1924
&lt;/span>&lt;span class="lnt">1925
&lt;/span>&lt;span class="lnt">1926
&lt;/span>&lt;span class="lnt">1927
&lt;/span>&lt;span class="lnt">1928
&lt;/span>&lt;span class="lnt">1929
&lt;/span>&lt;span class="lnt">1930
&lt;/span>&lt;span class="lnt">1931
&lt;/span>&lt;span class="lnt">1932
&lt;/span>&lt;span class="lnt">1933
&lt;/span>&lt;span class="lnt">1934
&lt;/span>&lt;span class="lnt">1935
&lt;/span>&lt;span class="lnt">1936
&lt;/span>&lt;span class="lnt">1937
&lt;/span>&lt;span class="lnt">1938
&lt;/span>&lt;span class="lnt">1939
&lt;/span>&lt;span class="lnt">1940
&lt;/span>&lt;span class="lnt">1941
&lt;/span>&lt;span class="lnt">1942
&lt;/span>&lt;span class="lnt">1943
&lt;/span>&lt;span class="lnt">1944
&lt;/span>&lt;span class="lnt">1945
&lt;/span>&lt;span class="lnt">1946
&lt;/span>&lt;span class="lnt">1947
&lt;/span>&lt;span class="lnt">1948
&lt;/span>&lt;span class="lnt">1949
&lt;/span>&lt;span class="lnt">1950
&lt;/span>&lt;span class="lnt">1951
&lt;/span>&lt;span class="lnt">1952
&lt;/span>&lt;span class="lnt">1953
&lt;/span>&lt;span class="lnt">1954
&lt;/span>&lt;span class="lnt">1955
&lt;/span>&lt;span class="lnt">1956
&lt;/span>&lt;span class="lnt">1957
&lt;/span>&lt;span class="lnt">1958
&lt;/span>&lt;span class="lnt">1959
&lt;/span>&lt;span class="lnt">1960
&lt;/span>&lt;span class="lnt">1961
&lt;/span>&lt;span class="lnt">1962
&lt;/span>&lt;span class="lnt">1963
&lt;/span>&lt;span class="lnt">1964
&lt;/span>&lt;span class="lnt">1965
&lt;/span>&lt;span class="lnt">1966
&lt;/span>&lt;span class="lnt">1967
&lt;/span>&lt;span class="lnt">1968
&lt;/span>&lt;span class="lnt">1969
&lt;/span>&lt;span class="lnt">1970
&lt;/span>&lt;span class="lnt">1971
&lt;/span>&lt;span class="lnt">1972
&lt;/span>&lt;span class="lnt">1973
&lt;/span>&lt;span class="lnt">1974
&lt;/span>&lt;span class="lnt">1975
&lt;/span>&lt;span class="lnt">1976
&lt;/span>&lt;span class="lnt">1977
&lt;/span>&lt;span class="lnt">1978
&lt;/span>&lt;span class="lnt">1979
&lt;/span>&lt;span class="lnt">1980
&lt;/span>&lt;span class="lnt">1981
&lt;/span>&lt;span class="lnt">1982
&lt;/span>&lt;span class="lnt">1983
&lt;/span>&lt;span class="lnt">1984
&lt;/span>&lt;span class="lnt">1985
&lt;/span>&lt;span class="lnt">1986
&lt;/span>&lt;span class="lnt">1987
&lt;/span>&lt;span class="lnt">1988
&lt;/span>&lt;span class="lnt">1989
&lt;/span>&lt;span class="lnt">1990
&lt;/span>&lt;span class="lnt">1991
&lt;/span>&lt;span class="lnt">1992
&lt;/span>&lt;span class="lnt">1993
&lt;/span>&lt;span class="lnt">1994
&lt;/span>&lt;span class="lnt">1995
&lt;/span>&lt;span class="lnt">1996
&lt;/span>&lt;span class="lnt">1997
&lt;/span>&lt;span class="lnt">1998
&lt;/span>&lt;span class="lnt">1999
&lt;/span>&lt;span class="lnt">2000
&lt;/span>&lt;span class="lnt">2001
&lt;/span>&lt;span class="lnt">2002
&lt;/span>&lt;span class="lnt">2003
&lt;/span>&lt;span class="lnt">2004
&lt;/span>&lt;span class="lnt">2005
&lt;/span>&lt;span class="lnt">2006
&lt;/span>&lt;span class="lnt">2007
&lt;/span>&lt;span class="lnt">2008
&lt;/span>&lt;span class="lnt">2009
&lt;/span>&lt;span class="lnt">2010
&lt;/span>&lt;span class="lnt">2011
&lt;/span>&lt;span class="lnt">2012
&lt;/span>&lt;span class="lnt">2013
&lt;/span>&lt;span class="lnt">2014
&lt;/span>&lt;span class="lnt">2015
&lt;/span>&lt;span class="lnt">2016
&lt;/span>&lt;span class="lnt">2017
&lt;/span>&lt;span class="lnt">2018
&lt;/span>&lt;span class="lnt">2019
&lt;/span>&lt;span class="lnt">2020
&lt;/span>&lt;span class="lnt">2021
&lt;/span>&lt;span class="lnt">2022
&lt;/span>&lt;span class="lnt">2023
&lt;/span>&lt;span class="lnt">2024
&lt;/span>&lt;span class="lnt">2025
&lt;/span>&lt;span class="lnt">2026
&lt;/span>&lt;span class="lnt">2027
&lt;/span>&lt;span class="lnt">2028
&lt;/span>&lt;span class="lnt">2029
&lt;/span>&lt;span class="lnt">2030
&lt;/span>&lt;span class="lnt">2031
&lt;/span>&lt;span class="lnt">2032
&lt;/span>&lt;span class="lnt">2033
&lt;/span>&lt;span class="lnt">2034
&lt;/span>&lt;span class="lnt">2035
&lt;/span>&lt;span class="lnt">2036
&lt;/span>&lt;span class="lnt">2037
&lt;/span>&lt;span class="lnt">2038
&lt;/span>&lt;span class="lnt">2039
&lt;/span>&lt;span class="lnt">2040
&lt;/span>&lt;span class="lnt">2041
&lt;/span>&lt;span class="lnt">2042
&lt;/span>&lt;span class="lnt">2043
&lt;/span>&lt;span class="lnt">2044
&lt;/span>&lt;span class="lnt">2045
&lt;/span>&lt;span class="lnt">2046
&lt;/span>&lt;span class="lnt">2047
&lt;/span>&lt;span class="lnt">2048
&lt;/span>&lt;span class="lnt">2049
&lt;/span>&lt;span class="lnt">2050
&lt;/span>&lt;span class="lnt">2051
&lt;/span>&lt;span class="lnt">2052
&lt;/span>&lt;span class="lnt">2053
&lt;/span>&lt;span class="lnt">2054
&lt;/span>&lt;span class="lnt">2055
&lt;/span>&lt;span class="lnt">2056
&lt;/span>&lt;span class="lnt">2057
&lt;/span>&lt;span class="lnt">2058
&lt;/span>&lt;span class="lnt">2059
&lt;/span>&lt;span class="lnt">2060
&lt;/span>&lt;span class="lnt">2061
&lt;/span>&lt;span class="lnt">2062
&lt;/span>&lt;span class="lnt">2063
&lt;/span>&lt;span class="lnt">2064
&lt;/span>&lt;span class="lnt">2065
&lt;/span>&lt;span class="lnt">2066
&lt;/span>&lt;span class="lnt">2067
&lt;/span>&lt;span class="lnt">2068
&lt;/span>&lt;span class="lnt">2069
&lt;/span>&lt;span class="lnt">2070
&lt;/span>&lt;span class="lnt">2071
&lt;/span>&lt;span class="lnt">2072
&lt;/span>&lt;span class="lnt">2073
&lt;/span>&lt;span class="lnt">2074
&lt;/span>&lt;span class="lnt">2075
&lt;/span>&lt;span class="lnt">2076
&lt;/span>&lt;span class="lnt">2077
&lt;/span>&lt;span class="lnt">2078
&lt;/span>&lt;span class="lnt">2079
&lt;/span>&lt;span class="lnt">2080
&lt;/span>&lt;span class="lnt">2081
&lt;/span>&lt;span class="lnt">2082
&lt;/span>&lt;span class="lnt">2083
&lt;/span>&lt;span class="lnt">2084
&lt;/span>&lt;span class="lnt">2085
&lt;/span>&lt;span class="lnt">2086
&lt;/span>&lt;span class="lnt">2087
&lt;/span>&lt;span class="lnt">2088
&lt;/span>&lt;span class="lnt">2089
&lt;/span>&lt;span class="lnt">2090
&lt;/span>&lt;span class="lnt">2091
&lt;/span>&lt;span class="lnt">2092
&lt;/span>&lt;span class="lnt">2093
&lt;/span>&lt;span class="lnt">2094
&lt;/span>&lt;span class="lnt">2095
&lt;/span>&lt;span class="lnt">2096
&lt;/span>&lt;span class="lnt">2097
&lt;/span>&lt;span class="lnt">2098
&lt;/span>&lt;span class="lnt">2099
&lt;/span>&lt;span class="lnt">2100
&lt;/span>&lt;span class="lnt">2101
&lt;/span>&lt;span class="lnt">2102
&lt;/span>&lt;span class="lnt">2103
&lt;/span>&lt;span class="lnt">2104
&lt;/span>&lt;span class="lnt">2105
&lt;/span>&lt;span class="lnt">2106
&lt;/span>&lt;span class="lnt">2107
&lt;/span>&lt;span class="lnt">2108
&lt;/span>&lt;span class="lnt">2109
&lt;/span>&lt;span class="lnt">2110
&lt;/span>&lt;span class="lnt">2111
&lt;/span>&lt;span class="lnt">2112
&lt;/span>&lt;span class="lnt">2113
&lt;/span>&lt;span class="lnt">2114
&lt;/span>&lt;span class="lnt">2115
&lt;/span>&lt;span class="lnt">2116
&lt;/span>&lt;span class="lnt">2117
&lt;/span>&lt;span class="lnt">2118
&lt;/span>&lt;span class="lnt">2119
&lt;/span>&lt;span class="lnt">2120
&lt;/span>&lt;span class="lnt">2121
&lt;/span>&lt;span class="lnt">2122
&lt;/span>&lt;span class="lnt">2123
&lt;/span>&lt;span class="lnt">2124
&lt;/span>&lt;span class="lnt">2125
&lt;/span>&lt;span class="lnt">2126
&lt;/span>&lt;span class="lnt">2127
&lt;/span>&lt;span class="lnt">2128
&lt;/span>&lt;span class="lnt">2129
&lt;/span>&lt;span class="lnt">2130
&lt;/span>&lt;span class="lnt">2131
&lt;/span>&lt;span class="lnt">2132
&lt;/span>&lt;span class="lnt">2133
&lt;/span>&lt;span class="lnt">2134
&lt;/span>&lt;span class="lnt">2135
&lt;/span>&lt;span class="lnt">2136
&lt;/span>&lt;span class="lnt">2137
&lt;/span>&lt;span class="lnt">2138
&lt;/span>&lt;span class="lnt">2139
&lt;/span>&lt;span class="lnt">2140
&lt;/span>&lt;span class="lnt">2141
&lt;/span>&lt;span class="lnt">2142
&lt;/span>&lt;span class="lnt">2143
&lt;/span>&lt;span class="lnt">2144
&lt;/span>&lt;span class="lnt">2145
&lt;/span>&lt;span class="lnt">2146
&lt;/span>&lt;span class="lnt">2147
&lt;/span>&lt;span class="lnt">2148
&lt;/span>&lt;span class="lnt">2149
&lt;/span>&lt;span class="lnt">2150
&lt;/span>&lt;span class="lnt">2151
&lt;/span>&lt;span class="lnt">2152
&lt;/span>&lt;span class="lnt">2153
&lt;/span>&lt;span class="lnt">2154
&lt;/span>&lt;span class="lnt">2155
&lt;/span>&lt;span class="lnt">2156
&lt;/span>&lt;span class="lnt">2157
&lt;/span>&lt;span class="lnt">2158
&lt;/span>&lt;span class="lnt">2159
&lt;/span>&lt;span class="lnt">2160
&lt;/span>&lt;span class="lnt">2161
&lt;/span>&lt;span class="lnt">2162
&lt;/span>&lt;span class="lnt">2163
&lt;/span>&lt;span class="lnt">2164
&lt;/span>&lt;span class="lnt">2165
&lt;/span>&lt;span class="lnt">2166
&lt;/span>&lt;span class="lnt">2167
&lt;/span>&lt;span class="lnt">2168
&lt;/span>&lt;span class="lnt">2169
&lt;/span>&lt;span class="lnt">2170
&lt;/span>&lt;span class="lnt">2171
&lt;/span>&lt;span class="lnt">2172
&lt;/span>&lt;span class="lnt">2173
&lt;/span>&lt;span class="lnt">2174
&lt;/span>&lt;span class="lnt">2175
&lt;/span>&lt;span class="lnt">2176
&lt;/span>&lt;span class="lnt">2177
&lt;/span>&lt;span class="lnt">2178
&lt;/span>&lt;span class="lnt">2179
&lt;/span>&lt;span class="lnt">2180
&lt;/span>&lt;span class="lnt">2181
&lt;/span>&lt;span class="lnt">2182
&lt;/span>&lt;span class="lnt">2183
&lt;/span>&lt;span class="lnt">2184
&lt;/span>&lt;span class="lnt">2185
&lt;/span>&lt;span class="lnt">2186
&lt;/span>&lt;span class="lnt">2187
&lt;/span>&lt;span class="lnt">2188
&lt;/span>&lt;span class="lnt">2189
&lt;/span>&lt;span class="lnt">2190
&lt;/span>&lt;span class="lnt">2191
&lt;/span>&lt;span class="lnt">2192
&lt;/span>&lt;span class="lnt">2193
&lt;/span>&lt;span class="lnt">2194
&lt;/span>&lt;span class="lnt">2195
&lt;/span>&lt;span class="lnt">2196
&lt;/span>&lt;span class="lnt">2197
&lt;/span>&lt;span class="lnt">2198
&lt;/span>&lt;span class="lnt">2199
&lt;/span>&lt;span class="lnt">2200
&lt;/span>&lt;span class="lnt">2201
&lt;/span>&lt;span class="lnt">2202
&lt;/span>&lt;span class="lnt">2203
&lt;/span>&lt;span class="lnt">2204
&lt;/span>&lt;span class="lnt">2205
&lt;/span>&lt;span class="lnt">2206
&lt;/span>&lt;span class="lnt">2207
&lt;/span>&lt;span class="lnt">2208
&lt;/span>&lt;span class="lnt">2209
&lt;/span>&lt;span class="lnt">2210
&lt;/span>&lt;span class="lnt">2211
&lt;/span>&lt;span class="lnt">2212
&lt;/span>&lt;span class="lnt">2213
&lt;/span>&lt;span class="lnt">2214
&lt;/span>&lt;span class="lnt">2215
&lt;/span>&lt;span class="lnt">2216
&lt;/span>&lt;span class="lnt">2217
&lt;/span>&lt;span class="lnt">2218
&lt;/span>&lt;span class="lnt">2219
&lt;/span>&lt;span class="lnt">2220
&lt;/span>&lt;span class="lnt">2221
&lt;/span>&lt;span class="lnt">2222
&lt;/span>&lt;span class="lnt">2223
&lt;/span>&lt;span class="lnt">2224
&lt;/span>&lt;span class="lnt">2225
&lt;/span>&lt;span class="lnt">2226
&lt;/span>&lt;span class="lnt">2227
&lt;/span>&lt;span class="lnt">2228
&lt;/span>&lt;span class="lnt">2229
&lt;/span>&lt;span class="lnt">2230
&lt;/span>&lt;span class="lnt">2231
&lt;/span>&lt;span class="lnt">2232
&lt;/span>&lt;span class="lnt">2233
&lt;/span>&lt;span class="lnt">2234
&lt;/span>&lt;span class="lnt">2235
&lt;/span>&lt;span class="lnt">2236
&lt;/span>&lt;span class="lnt">2237
&lt;/span>&lt;span class="lnt">2238
&lt;/span>&lt;span class="lnt">2239
&lt;/span>&lt;span class="lnt">2240
&lt;/span>&lt;span class="lnt">2241
&lt;/span>&lt;span class="lnt">2242
&lt;/span>&lt;span class="lnt">2243
&lt;/span>&lt;span class="lnt">2244
&lt;/span>&lt;span class="lnt">2245
&lt;/span>&lt;span class="lnt">2246
&lt;/span>&lt;span class="lnt">2247
&lt;/span>&lt;span class="lnt">2248
&lt;/span>&lt;span class="lnt">2249
&lt;/span>&lt;span class="lnt">2250
&lt;/span>&lt;span class="lnt">2251
&lt;/span>&lt;span class="lnt">2252
&lt;/span>&lt;span class="lnt">2253
&lt;/span>&lt;span class="lnt">2254
&lt;/span>&lt;span class="lnt">2255
&lt;/span>&lt;span class="lnt">2256
&lt;/span>&lt;span class="lnt">2257
&lt;/span>&lt;span class="lnt">2258
&lt;/span>&lt;span class="lnt">2259
&lt;/span>&lt;span class="lnt">2260
&lt;/span>&lt;span class="lnt">2261
&lt;/span>&lt;span class="lnt">2262
&lt;/span>&lt;span class="lnt">2263
&lt;/span>&lt;span class="lnt">2264
&lt;/span>&lt;span class="lnt">2265
&lt;/span>&lt;span class="lnt">2266
&lt;/span>&lt;span class="lnt">2267
&lt;/span>&lt;span class="lnt">2268
&lt;/span>&lt;span class="lnt">2269
&lt;/span>&lt;span class="lnt">2270
&lt;/span>&lt;span class="lnt">2271
&lt;/span>&lt;span class="lnt">2272
&lt;/span>&lt;span class="lnt">2273
&lt;/span>&lt;span class="lnt">2274
&lt;/span>&lt;span class="lnt">2275
&lt;/span>&lt;span class="lnt">2276
&lt;/span>&lt;span class="lnt">2277
&lt;/span>&lt;span class="lnt">2278
&lt;/span>&lt;span class="lnt">2279
&lt;/span>&lt;span class="lnt">2280
&lt;/span>&lt;span class="lnt">2281
&lt;/span>&lt;span class="lnt">2282
&lt;/span>&lt;span class="lnt">2283
&lt;/span>&lt;span class="lnt">2284
&lt;/span>&lt;span class="lnt">2285
&lt;/span>&lt;span class="lnt">2286
&lt;/span>&lt;span class="lnt">2287
&lt;/span>&lt;span class="lnt">2288
&lt;/span>&lt;span class="lnt">2289
&lt;/span>&lt;span class="lnt">2290
&lt;/span>&lt;span class="lnt">2291
&lt;/span>&lt;span class="lnt">2292
&lt;/span>&lt;span class="lnt">2293
&lt;/span>&lt;span class="lnt">2294
&lt;/span>&lt;span class="lnt">2295
&lt;/span>&lt;span class="lnt">2296
&lt;/span>&lt;span class="lnt">2297
&lt;/span>&lt;span class="lnt">2298
&lt;/span>&lt;span class="lnt">2299
&lt;/span>&lt;span class="lnt">2300
&lt;/span>&lt;span class="lnt">2301
&lt;/span>&lt;span class="lnt">2302
&lt;/span>&lt;span class="lnt">2303
&lt;/span>&lt;span class="lnt">2304
&lt;/span>&lt;span class="lnt">2305
&lt;/span>&lt;span class="lnt">2306
&lt;/span>&lt;span class="lnt">2307
&lt;/span>&lt;span class="lnt">2308
&lt;/span>&lt;span class="lnt">2309
&lt;/span>&lt;span class="lnt">2310
&lt;/span>&lt;span class="lnt">2311
&lt;/span>&lt;span class="lnt">2312
&lt;/span>&lt;span class="lnt">2313
&lt;/span>&lt;span class="lnt">2314
&lt;/span>&lt;span class="lnt">2315
&lt;/span>&lt;span class="lnt">2316
&lt;/span>&lt;span class="lnt">2317
&lt;/span>&lt;span class="lnt">2318
&lt;/span>&lt;span class="lnt">2319
&lt;/span>&lt;span class="lnt">2320
&lt;/span>&lt;span class="lnt">2321
&lt;/span>&lt;span class="lnt">2322
&lt;/span>&lt;span class="lnt">2323
&lt;/span>&lt;span class="lnt">2324
&lt;/span>&lt;span class="lnt">2325
&lt;/span>&lt;span class="lnt">2326
&lt;/span>&lt;span class="lnt">2327
&lt;/span>&lt;span class="lnt">2328
&lt;/span>&lt;span class="lnt">2329
&lt;/span>&lt;span class="lnt">2330
&lt;/span>&lt;span class="lnt">2331
&lt;/span>&lt;span class="lnt">2332
&lt;/span>&lt;span class="lnt">2333
&lt;/span>&lt;span class="lnt">2334
&lt;/span>&lt;span class="lnt">2335
&lt;/span>&lt;span class="lnt">2336
&lt;/span>&lt;span class="lnt">2337
&lt;/span>&lt;span class="lnt">2338
&lt;/span>&lt;span class="lnt">2339
&lt;/span>&lt;span class="lnt">2340
&lt;/span>&lt;span class="lnt">2341
&lt;/span>&lt;span class="lnt">2342
&lt;/span>&lt;span class="lnt">2343
&lt;/span>&lt;span class="lnt">2344
&lt;/span>&lt;span class="lnt">2345
&lt;/span>&lt;span class="lnt">2346
&lt;/span>&lt;span class="lnt">2347
&lt;/span>&lt;span class="lnt">2348
&lt;/span>&lt;span class="lnt">2349
&lt;/span>&lt;span class="lnt">2350
&lt;/span>&lt;span class="lnt">2351
&lt;/span>&lt;span class="lnt">2352
&lt;/span>&lt;span class="lnt">2353
&lt;/span>&lt;span class="lnt">2354
&lt;/span>&lt;span class="lnt">2355
&lt;/span>&lt;span class="lnt">2356
&lt;/span>&lt;span class="lnt">2357
&lt;/span>&lt;span class="lnt">2358
&lt;/span>&lt;span class="lnt">2359
&lt;/span>&lt;span class="lnt">2360
&lt;/span>&lt;span class="lnt">2361
&lt;/span>&lt;span class="lnt">2362
&lt;/span>&lt;span class="lnt">2363
&lt;/span>&lt;span class="lnt">2364
&lt;/span>&lt;span class="lnt">2365
&lt;/span>&lt;span class="lnt">2366
&lt;/span>&lt;span class="lnt">2367
&lt;/span>&lt;span class="lnt">2368
&lt;/span>&lt;span class="lnt">2369
&lt;/span>&lt;span class="lnt">2370
&lt;/span>&lt;span class="lnt">2371
&lt;/span>&lt;span class="lnt">2372
&lt;/span>&lt;span class="lnt">2373
&lt;/span>&lt;span class="lnt">2374
&lt;/span>&lt;span class="lnt">2375
&lt;/span>&lt;span class="lnt">2376
&lt;/span>&lt;span class="lnt">2377
&lt;/span>&lt;span class="lnt">2378
&lt;/span>&lt;span class="lnt">2379
&lt;/span>&lt;span class="lnt">2380
&lt;/span>&lt;span class="lnt">2381
&lt;/span>&lt;span class="lnt">2382
&lt;/span>&lt;span class="lnt">2383
&lt;/span>&lt;span class="lnt">2384
&lt;/span>&lt;span class="lnt">2385
&lt;/span>&lt;span class="lnt">2386
&lt;/span>&lt;span class="lnt">2387
&lt;/span>&lt;span class="lnt">2388
&lt;/span>&lt;span class="lnt">2389
&lt;/span>&lt;span class="lnt">2390
&lt;/span>&lt;span class="lnt">2391
&lt;/span>&lt;span class="lnt">2392
&lt;/span>&lt;span class="lnt">2393
&lt;/span>&lt;span class="lnt">2394
&lt;/span>&lt;span class="lnt">2395
&lt;/span>&lt;span class="lnt">2396
&lt;/span>&lt;span class="lnt">2397
&lt;/span>&lt;span class="lnt">2398
&lt;/span>&lt;span class="lnt">2399
&lt;/span>&lt;span class="lnt">2400
&lt;/span>&lt;span class="lnt">2401
&lt;/span>&lt;span class="lnt">2402
&lt;/span>&lt;span class="lnt">2403
&lt;/span>&lt;span class="lnt">2404
&lt;/span>&lt;span class="lnt">2405
&lt;/span>&lt;span class="lnt">2406
&lt;/span>&lt;span class="lnt">2407
&lt;/span>&lt;span class="lnt">2408
&lt;/span>&lt;span class="lnt">2409
&lt;/span>&lt;span class="lnt">2410
&lt;/span>&lt;span class="lnt">2411
&lt;/span>&lt;span class="lnt">2412
&lt;/span>&lt;span class="lnt">2413
&lt;/span>&lt;span class="lnt">2414
&lt;/span>&lt;span class="lnt">2415
&lt;/span>&lt;span class="lnt">2416
&lt;/span>&lt;span class="lnt">2417
&lt;/span>&lt;span class="lnt">2418
&lt;/span>&lt;span class="lnt">2419
&lt;/span>&lt;span class="lnt">2420
&lt;/span>&lt;span class="lnt">2421
&lt;/span>&lt;span class="lnt">2422
&lt;/span>&lt;span class="lnt">2423
&lt;/span>&lt;span class="lnt">2424
&lt;/span>&lt;span class="lnt">2425
&lt;/span>&lt;span class="lnt">2426
&lt;/span>&lt;span class="lnt">2427
&lt;/span>&lt;span class="lnt">2428
&lt;/span>&lt;span class="lnt">2429
&lt;/span>&lt;span class="lnt">2430
&lt;/span>&lt;span class="lnt">2431
&lt;/span>&lt;span class="lnt">2432
&lt;/span>&lt;span class="lnt">2433
&lt;/span>&lt;span class="lnt">2434
&lt;/span>&lt;span class="lnt">2435
&lt;/span>&lt;span class="lnt">2436
&lt;/span>&lt;span class="lnt">2437
&lt;/span>&lt;span class="lnt">2438
&lt;/span>&lt;span class="lnt">2439
&lt;/span>&lt;span class="lnt">2440
&lt;/span>&lt;span class="lnt">2441
&lt;/span>&lt;span class="lnt">2442
&lt;/span>&lt;span class="lnt">2443
&lt;/span>&lt;span class="lnt">2444
&lt;/span>&lt;span class="lnt">2445
&lt;/span>&lt;span class="lnt">2446
&lt;/span>&lt;span class="lnt">2447
&lt;/span>&lt;span class="lnt">2448
&lt;/span>&lt;span class="lnt">2449
&lt;/span>&lt;span class="lnt">2450
&lt;/span>&lt;span class="lnt">2451
&lt;/span>&lt;span class="lnt">2452
&lt;/span>&lt;span class="lnt">2453
&lt;/span>&lt;span class="lnt">2454
&lt;/span>&lt;span class="lnt">2455
&lt;/span>&lt;span class="lnt">2456
&lt;/span>&lt;span class="lnt">2457
&lt;/span>&lt;span class="lnt">2458
&lt;/span>&lt;span class="lnt">2459
&lt;/span>&lt;span class="lnt">2460
&lt;/span>&lt;span class="lnt">2461
&lt;/span>&lt;span class="lnt">2462
&lt;/span>&lt;span class="lnt">2463
&lt;/span>&lt;span class="lnt">2464
&lt;/span>&lt;span class="lnt">2465
&lt;/span>&lt;span class="lnt">2466
&lt;/span>&lt;span class="lnt">2467
&lt;/span>&lt;span class="lnt">2468
&lt;/span>&lt;span class="lnt">2469
&lt;/span>&lt;span class="lnt">2470
&lt;/span>&lt;span class="lnt">2471
&lt;/span>&lt;span class="lnt">2472
&lt;/span>&lt;span class="lnt">2473
&lt;/span>&lt;span class="lnt">2474
&lt;/span>&lt;span class="lnt">2475
&lt;/span>&lt;span class="lnt">2476
&lt;/span>&lt;span class="lnt">2477
&lt;/span>&lt;span class="lnt">2478
&lt;/span>&lt;span class="lnt">2479
&lt;/span>&lt;span class="lnt">2480
&lt;/span>&lt;span class="lnt">2481
&lt;/span>&lt;span class="lnt">2482
&lt;/span>&lt;span class="lnt">2483
&lt;/span>&lt;span class="lnt">2484
&lt;/span>&lt;span class="lnt">2485
&lt;/span>&lt;span class="lnt">2486
&lt;/span>&lt;span class="lnt">2487
&lt;/span>&lt;span class="lnt">2488
&lt;/span>&lt;span class="lnt">2489
&lt;/span>&lt;span class="lnt">2490
&lt;/span>&lt;span class="lnt">2491
&lt;/span>&lt;span class="lnt">2492
&lt;/span>&lt;span class="lnt">2493
&lt;/span>&lt;span class="lnt">2494
&lt;/span>&lt;span class="lnt">2495
&lt;/span>&lt;span class="lnt">2496
&lt;/span>&lt;span class="lnt">2497
&lt;/span>&lt;span class="lnt">2498
&lt;/span>&lt;span class="lnt">2499
&lt;/span>&lt;span class="lnt">2500
&lt;/span>&lt;span class="lnt">2501
&lt;/span>&lt;span class="lnt">2502
&lt;/span>&lt;span class="lnt">2503
&lt;/span>&lt;span class="lnt">2504
&lt;/span>&lt;span class="lnt">2505
&lt;/span>&lt;span class="lnt">2506
&lt;/span>&lt;span class="lnt">2507
&lt;/span>&lt;span class="lnt">2508
&lt;/span>&lt;span class="lnt">2509
&lt;/span>&lt;span class="lnt">2510
&lt;/span>&lt;span class="lnt">2511
&lt;/span>&lt;span class="lnt">2512
&lt;/span>&lt;span class="lnt">2513
&lt;/span>&lt;span class="lnt">2514
&lt;/span>&lt;span class="lnt">2515
&lt;/span>&lt;span class="lnt">2516
&lt;/span>&lt;span class="lnt">2517
&lt;/span>&lt;span class="lnt">2518
&lt;/span>&lt;span class="lnt">2519
&lt;/span>&lt;span class="lnt">2520
&lt;/span>&lt;span class="lnt">2521
&lt;/span>&lt;span class="lnt">2522
&lt;/span>&lt;span class="lnt">2523
&lt;/span>&lt;span class="lnt">2524
&lt;/span>&lt;span class="lnt">2525
&lt;/span>&lt;span class="lnt">2526
&lt;/span>&lt;span class="lnt">2527
&lt;/span>&lt;span class="lnt">2528
&lt;/span>&lt;span class="lnt">2529
&lt;/span>&lt;span class="lnt">2530
&lt;/span>&lt;span class="lnt">2531
&lt;/span>&lt;span class="lnt">2532
&lt;/span>&lt;span class="lnt">2533
&lt;/span>&lt;span class="lnt">2534
&lt;/span>&lt;span class="lnt">2535
&lt;/span>&lt;span class="lnt">2536
&lt;/span>&lt;span class="lnt">2537
&lt;/span>&lt;span class="lnt">2538
&lt;/span>&lt;span class="lnt">2539
&lt;/span>&lt;span class="lnt">2540
&lt;/span>&lt;span class="lnt">2541
&lt;/span>&lt;span class="lnt">2542
&lt;/span>&lt;span class="lnt">2543
&lt;/span>&lt;span class="lnt">2544
&lt;/span>&lt;span class="lnt">2545
&lt;/span>&lt;span class="lnt">2546
&lt;/span>&lt;span class="lnt">2547
&lt;/span>&lt;span class="lnt">2548
&lt;/span>&lt;span class="lnt">2549
&lt;/span>&lt;span class="lnt">2550
&lt;/span>&lt;span class="lnt">2551
&lt;/span>&lt;span class="lnt">2552
&lt;/span>&lt;span class="lnt">2553
&lt;/span>&lt;span class="lnt">2554
&lt;/span>&lt;span class="lnt">2555
&lt;/span>&lt;span class="lnt">2556
&lt;/span>&lt;span class="lnt">2557
&lt;/span>&lt;span class="lnt">2558
&lt;/span>&lt;span class="lnt">2559
&lt;/span>&lt;span class="lnt">2560
&lt;/span>&lt;span class="lnt">2561
&lt;/span>&lt;span class="lnt">2562
&lt;/span>&lt;span class="lnt">2563
&lt;/span>&lt;span class="lnt">2564
&lt;/span>&lt;span class="lnt">2565
&lt;/span>&lt;span class="lnt">2566
&lt;/span>&lt;span class="lnt">2567
&lt;/span>&lt;span class="lnt">2568
&lt;/span>&lt;span class="lnt">2569
&lt;/span>&lt;span class="lnt">2570
&lt;/span>&lt;span class="lnt">2571
&lt;/span>&lt;span class="lnt">2572
&lt;/span>&lt;span class="lnt">2573
&lt;/span>&lt;span class="lnt">2574
&lt;/span>&lt;span class="lnt">2575
&lt;/span>&lt;span class="lnt">2576
&lt;/span>&lt;span class="lnt">2577
&lt;/span>&lt;span class="lnt">2578
&lt;/span>&lt;span class="lnt">2579
&lt;/span>&lt;span class="lnt">2580
&lt;/span>&lt;span class="lnt">2581
&lt;/span>&lt;span class="lnt">2582
&lt;/span>&lt;span class="lnt">2583
&lt;/span>&lt;span class="lnt">2584
&lt;/span>&lt;span class="lnt">2585
&lt;/span>&lt;span class="lnt">2586
&lt;/span>&lt;span class="lnt">2587
&lt;/span>&lt;span class="lnt">2588
&lt;/span>&lt;span class="lnt">2589
&lt;/span>&lt;span class="lnt">2590
&lt;/span>&lt;span class="lnt">2591
&lt;/span>&lt;span class="lnt">2592
&lt;/span>&lt;span class="lnt">2593
&lt;/span>&lt;span class="lnt">2594
&lt;/span>&lt;span class="lnt">2595
&lt;/span>&lt;span class="lnt">2596
&lt;/span>&lt;span class="lnt">2597
&lt;/span>&lt;span class="lnt">2598
&lt;/span>&lt;span class="lnt">2599
&lt;/span>&lt;span class="lnt">2600
&lt;/span>&lt;span class="lnt">2601
&lt;/span>&lt;span class="lnt">2602
&lt;/span>&lt;span class="lnt">2603
&lt;/span>&lt;span class="lnt">2604
&lt;/span>&lt;span class="lnt">2605
&lt;/span>&lt;span class="lnt">2606
&lt;/span>&lt;span class="lnt">2607
&lt;/span>&lt;span class="lnt">2608
&lt;/span>&lt;span class="lnt">2609
&lt;/span>&lt;span class="lnt">2610
&lt;/span>&lt;span class="lnt">2611
&lt;/span>&lt;span class="lnt">2612
&lt;/span>&lt;span class="lnt">2613
&lt;/span>&lt;span class="lnt">2614
&lt;/span>&lt;span class="lnt">2615
&lt;/span>&lt;span class="lnt">2616
&lt;/span>&lt;span class="lnt">2617
&lt;/span>&lt;span class="lnt">2618
&lt;/span>&lt;span class="lnt">2619
&lt;/span>&lt;span class="lnt">2620
&lt;/span>&lt;span class="lnt">2621
&lt;/span>&lt;span class="lnt">2622
&lt;/span>&lt;span class="lnt">2623
&lt;/span>&lt;span class="lnt">2624
&lt;/span>&lt;span class="lnt">2625
&lt;/span>&lt;span class="lnt">2626
&lt;/span>&lt;span class="lnt">2627
&lt;/span>&lt;span class="lnt">2628
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-plain" data-lang="plain">&lt;span class="line">&lt;span class="cl">File: APPNOTE.TXT - .ZIP File Format Specification
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Version: 6.2.0 - NOTIFICATION OF CHANGE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Revised: 04/26/2004
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Copyright (c) 1989 - 2004 PKWARE Inc., All Rights Reserved.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">I. Purpose
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">This specification is intended to define a cross-platform,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">interoperable file format. Since its first publication
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">in 1989, PKWARE has remained committed to ensuring the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">interoperability of the .ZIP file format through this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">specification. We trust that all .ZIP compatible vendors
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">and application developers that have adopted this format
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">will share and support this commitment.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">II. Disclaimer
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Although PKWARE will attempt to supply current and accurate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">information relating to its file formats, algorithms, and the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">subject programs, the possibility of error or omission can not 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">be eliminated. PKWARE therefore expressly disclaims any warranty 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">that the information contained in the associated materials relating 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to the subject programs and/or the format of the files created or
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">accessed by the subject programs and/or the algorithms used by
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the subject programs, or any other matter, is current, correct or
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">accurate as delivered. Any risk of damage due to any possible
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">inaccurate information is assumed by the user of the information.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Furthermore, the information relating to the subject programs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">and/or the file formats created or accessed by the subject
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">programs and/or the algorithms used by the subject programs is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">subject to change without notice.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">If the version of this file is marked as a NOTIFICATION OF CHANGE,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the content defines an Early Feature Specification (EFS) change 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to the .ZIP file format that may be subject to modification prior 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to publication of the Final Feature Specification (FFS). This
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">document may also contain information on Planned Feature 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Specifications (PFS) defining recognized future extensions.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">III. Change Log
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Version Change Description Date
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">------- ------------------ ----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5.2 -Single Password Symmetric Encryption 07/16/2003
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> storage
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6.1.0 -Smart Card compatibility 01/20/2004
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -Documentation on certificate storage
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6.2.0 -Introduction of Central Directory 04/26/2004
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Encryption for encrypting metadata
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -Added OS/X to Version Made By values
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">VI. General Format of a .ZIP file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Files stored in arbitrary order. Large .ZIP files can span multiple
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> diskette media or be split into user-defined segment sizes. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Overall .ZIP file format:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [local file header 1]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [file data 1]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [data descriptor 1]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> . 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [local file header n]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [file data n]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [data descriptor n]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [archive decryption header] (EFS)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [archive extra data record] (EFS)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [central directory]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [zip64 end of central directory record]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [zip64 end of central directory locator] 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [end of central directory record]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> A. Local file header:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> local file header signature 4 bytes (0x04034b50)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> version needed to extract 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> general purpose bit flag 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compression method 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> last mod file time 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> last mod file date 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> crc-32 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compressed size 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> uncompressed size 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file name length 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> extra field length 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file name (variable size)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> extra field (variable size)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> B. File data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Immediately following the local header for a file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is the compressed or stored data for the file. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The series of [local file header][file data][data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> descriptor] repeats for each file in the .ZIP archive. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> C. Data descriptor:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> crc-32 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compressed size 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> uncompressed size 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This descriptor exists only if bit 3 of the general
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> purpose bit flag is set (see below). It is byte aligned
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and immediately follows the last byte of compressed data.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This descriptor is used only when it was not possible to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> seek in the output .ZIP file, e.g., when the output .ZIP file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> was standard output or a non seekable device. For Zip64 format
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> archives, the compressed and uncompressed sizes are 8 bytes each.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> D. Archive decryption header: (EFS)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The Archive Decryption Header is introduced in version 6.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of the ZIP format specification. This record exists in support
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of the Central Directory Encryption Feature implemented as part of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the Strong Encryption Specification as described in this document.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> When the Central Directory Structure is encrypted, this decryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> header will precede the encrypted data segment. The encrypted
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> data segment will consist of the Archive extra data record (if
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> present) and the encrypted Central Directory Structure data.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The format of this data record is identical to the Decryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> header record preceding compressed file data. If the central 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory structure is encrypted, the location of the start of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this data record is determined using the Start of Central Directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> field in the Zip64 End of Central Directory record. Refer to the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> section on the Strong Encryption Specification for information
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> on the fields used in the Archive Decryption Header record.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> E. Archive extra data record: (EFS)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> archive extra data signature 4 bytes (0x08064b50)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> extra field length 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> extra field data (variable size)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The Archive Extra Data Record is introduced in version 6.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of the ZIP format specification. This record exists in support
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of the Central Directory Encryption Feature implemented as part of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the Strong Encryption Specification as described in this document.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> When present, this record immediately precedes the central 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory data structure. The size of this data record will be
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> included in the Size of the Central Directory field in the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> End of Central Directory record. If the central directory structure
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is compressed, but not encrypted, the location of the start of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this data record is determined using the Start of Central Directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> field in the Zip64 End of Central Directory record. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> F. Central directory structure:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [file header 1]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> . 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [file header n]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> [digital signature] 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> File header:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> central file header signature 4 bytes (0x02014b50)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> version made by 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> version needed to extract 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> general purpose bit flag 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compression method 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> last mod file time 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> last mod file date 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> crc-32 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compressed size 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> uncompressed size 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file name length 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> extra field length 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file comment length 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> disk number start 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> internal file attributes 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> external file attributes 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> relative offset of local header 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file name (variable size)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> extra field (variable size)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file comment (variable size)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Digital signature:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> header signature 4 bytes (0x05054b50)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> size of data 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> signature data (variable size)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> With the introduction of the Central Directory Encryption 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> feature in version 6.2 of this specification, the Central 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Directory Structure may be stored both compressed and encrypted. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Although not required, it is assumed when encrypting the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Central Directory Structure, that it will be compressed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for greater storage efficiency. Information on the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Central Directory Encryption feature can be found in the section
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> describing the Strong Encryption Specification. The Digital 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Signature record will be neither compressed nor encrypted.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> G. Zip64 end of central directory record
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> zip64 end of central dir 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> signature 4 bytes (0x06064b50)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> size of zip64 end of central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory record 8 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> version made by 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> version needed to extract 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> number of this disk 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> number of the disk with the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> start of the central directory 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> total number of entries in the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> central directory on this disk 8 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> total number of entries in the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> central directory 8 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> size of the central directory 8 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> offset of start of central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory with respect to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the starting disk number 8 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> zip64 extensible data sector (variable size)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The above record structure defines Version 1 of the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Zip64 end of central directory record. Version 1 was 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> implemented in versions of this specification preceding 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6.2 in support of the ZIP64(tm) large file feature. The 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> introduction of the Central Directory Encryption feature 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> implemented in version 6.2 as part of the Strong Encryption 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Specification defines Version 2 of this record structure. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Refer to the section describing the Strong Encryption 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Specification for details on the version 2 format for 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this record.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> H. Zip64 end of central directory locator
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> zip64 end of central dir locator 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> signature 4 bytes (0x07064b50)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> number of the disk with the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> start of the zip64 end of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> central directory 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> relative offset of the zip64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> end of central directory record 8 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> total number of disks 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> I. End of central directory record:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> end of central dir signature 4 bytes (0x06054b50)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> number of this disk 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> number of the disk with the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> start of the central directory 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> total number of entries in the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> central directory on this disk 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> total number of entries in
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the central directory 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> size of the central directory 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> offset of start of central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory with respect to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the starting disk number 4 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .ZIP file comment length 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .ZIP file comment (variable size)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> J. Explanation of fields:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> version made by (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The upper byte indicates the compatibility of the file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> attribute information. If the external file attributes 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> are compatible with MS-DOS and can be read by PKZIP for 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> DOS version 2.04g then this value will be zero. If these 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> attributes are not compatible, then this value will 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> identify the host system on which the attributes are 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compatible. Software can use this information to determine
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the line record format for text files etc. The current
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mappings are:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 - Amiga 2 - OpenVMS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 - Unix 4 - VM/CMS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 - Atari ST 6 - OS/2 H.P.F.S.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 - Macintosh 8 - Z-System
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 9 - CP/M 10 - Windows NTFS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 11 - MVS (OS/390 - Z/OS) 12 - VSE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 13 - Acorn Risc 14 - VFAT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 15 - alternate MVS 16 - BeOS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 17 - Tandem 18 - OS/400
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 19 - OS/X (Darwin) 20 thru 255 - unused
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The lower byte indicates the ZIP specification version 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (the version of this document) supported by the software 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> used to encode the file. The value/10 indicates the major 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> version number, and the value mod 10 is the minor version 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> number. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> version needed to extract (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The minimum supported ZIP specification version needed to 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> extract the file, mapped as above. This value is based on 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the specific format features a ZIP program must support to 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> be able to extract the file. If multiple features are
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> applied to a file, the minimum version should be set to the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> feature having the highest value. New features or feature 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> changes affecting the published format specification will be 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> implemented using higher version numbers than the last 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> published value to avoid conflict.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Current minimum feature versions are as defined below:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1.0 - Default value
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1.1 - File is a volume label
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2.0 - File is a folder (directory)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2.0 - File is compressed using Deflate compression
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2.0 - File is encrypted using traditional PKWARE encryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2.1 - File is compressed using Deflate64(tm)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2.5 - File is compressed using PKWARE DCL Implode 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2.7 - File is a patch data set 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4.5 - File uses ZIP64 format extensions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4.6 - File is compressed using BZIP2 compression*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5.0 - File is encrypted using DES
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5.0 - File is encrypted using 3DES
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5.0 - File is encrypted using original RC2 encryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5.0 - File is encrypted using RC4 encryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5.1 - File is encrypted using AES encryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5.1 - File is encrypted using corrected RC2 encryption**
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5.2 - File is encrypted using corrected RC2-64 encryption**
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6.1 - File is encrypted using non-OAEP key wrapping***
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6.2 - Central directory encryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * Early 7.x (pre-7.2) versions of PKZIP incorrectly set the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> version needed to extract for BZIP2 compression to be 50
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> when it should have been 46.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ** Refer to the section on Strong Encryption Specification
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for additional information regarding RC2 corrections.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> *** Certificate encryption using non-OAEP key wrapping is the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> intended mode of operation for all versions beginning with 6.1.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Support for OAEP key wrapping should only be used for
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> backward compatibility when sending ZIP files to be opened by
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> versions of PKZIP older than 6.1 (5.0 or 6.0).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> When using ZIP64 extensions, the corresponding value in the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Zip64 end of central directory record should also be set. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This field currently supports only the value 45 to indicate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ZIP64 extensions are present.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> general purpose bit flag: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 0: If set, indicates that the file is encrypted.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (For Method 6 - Imploding)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 1: If the compression method used was type 6,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Imploding, then this bit, if set, indicates
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> an 8K sliding dictionary was used. If clear,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> then a 4K sliding dictionary was used.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 2: If the compression method used was type 6,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Imploding, then this bit, if set, indicates
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 Shannon-Fano trees were used to encode the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sliding dictionary output. If clear, then 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Shannon-Fano trees were used.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (For Methods 8 and 9 - Deflating)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 2 Bit 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 0 Normal (-en) compression option was used.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 1 Maximum (-exx/-ex) compression option was used.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 0 Fast (-ef) compression option was used.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 1 Super Fast (-es) compression option was used.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: Bits 1 and 2 are undefined if the compression
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> method is any other.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 3: If this bit is set, the fields crc-32, compressed 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> size and uncompressed size are set to zero in the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> local header. The correct values are put in the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> data descriptor immediately following the compressed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> data. (Note: PKZIP version 2.04g for DOS only 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> recognizes this bit for method 8 compression, newer 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> versions of PKZIP recognize this bit for any 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compression method.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 4: Reserved for use with method 8, for enhanced
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> deflating. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 5: If this bit is set, this indicates that the file is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compressed patched data. (Note: Requires PKZIP 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> version 2.70 or greater)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 6: Strong encryption. If this bit is set, you should
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> set the version needed to extract value to at least
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 50 and you must also set bit 0. If AES encryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is used, the version needed to extract value must 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> be at least 51.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 7: Currently unused.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 8: Currently unused.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 9: Currently unused.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 10: Currently unused.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 11: Currently unused.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 12: Reserved by PKWARE for enhanced compression.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 13: Used when encrypting the Central Directory to indicate 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> selected data values in the Local Header are masked to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> hide their actual values. See the section describing 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the Strong Encryption Specification for details.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 14: Reserved by PKWARE.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 15: Reserved by PKWARE.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compression method: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (see accompanying documentation for algorithm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> descriptions)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 - The file is stored (no compression)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 - The file is Shrunk
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 - The file is Reduced with compression factor 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 - The file is Reduced with compression factor 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 - The file is Reduced with compression factor 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 - The file is Reduced with compression factor 4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 - The file is Imploded
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 - Reserved for Tokenizing compression algorithm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 8 - The file is Deflated
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 9 - Enhanced Deflating using Deflate64(tm)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 10 - PKWARE Data Compression Library Imploding
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 11 - Reserved by PKWARE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 12 - File is compressed using BZIP2 algorithm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> date and time fields: (2 bytes each)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The date and time are encoded in standard MS-DOS format.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> If input came from standard input, the date and time are
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> those at which compression was started for this data. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> If encrypting the central directory and general purpose bit 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> flag 13 is set indicating masking, the value stored in the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Local Header will be zero. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CRC-32: (4 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The CRC-32 algorithm was generously contributed by
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> David Schwaderer and can be found in his excellent
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> book &amp;#34;C Programmers Guide to NetBIOS&amp;#34; published by
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Howard W. Sams &amp;amp; Co. Inc. The &amp;#39;magic number&amp;#39; for
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the CRC is 0xdebb20e3. The proper CRC pre and post
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> conditioning is used, meaning that the CRC register
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is pre-conditioned with all ones (a starting value
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of 0xffffffff) and the value is post-conditioned by
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> taking the one&amp;#39;s complement of the CRC residual.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> If bit 3 of the general purpose flag is set, this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> field is set to zero in the local header and the correct
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> value is put in the data descriptor and in the central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory. If encrypting the central directory and general 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> purpose bit flag 13 is set indicating masking, the value 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> stored in the Local Header will be zero. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compressed size: (4 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> uncompressed size: (4 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The size of the file compressed and uncompressed,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> respectively. If bit 3 of the general purpose bit flag
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is set, these fields are set to zero in the local header
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and the correct values are put in the data descriptor and
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in the central directory. If an archive is in zip64 format
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and the value in this field is 0xFFFFFFFF, the size will be
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in the corresponding 8 byte zip64 extended information 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> extra field. If encrypting the central directory and general 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> purpose bit flag 13 is set indicating masking, the value stored 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for the uncompressed size in the Local Header will be zero. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file name length: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> extra field length: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file comment length: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The length of the file name, extra field, and comment
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> fields respectively. The combined length of any
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory record and these three fields should not
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> generally exceed 65,535 bytes. If input came from standard
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> input, the file name length is set to zero. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> disk number start: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The number of the disk on which this file begins. If an 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> archive is in zip64 format and the value in this field is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0xFFFF, the size will be in the corresponding 4 byte zip64 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> extended information extra field.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> internal file attributes: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bits 1 and 2 are reserved for use by PKWARE.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The lowest bit of this field indicates, if set, that
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the file is apparently an ASCII or text file. If not
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> set, that the file apparently contains binary data.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The remaining bits are unused in version 1.0.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The 0x0002 bit of this field indicates, if set, that a 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 byte variable record length control field precedes each 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> logical record indicating the length of the record. This 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> flag is independent of text control characters, and if used 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in conjunction with text data, includes any control 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> characters in the total length of the record. This value is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> provided for mainframe data transfer support.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> external file attributes: (4 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The mapping of the external attributes is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> host-system dependent (see &amp;#39;version made by&amp;#39;). For
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> MS-DOS, the low order byte is the MS-DOS directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> attribute byte. If input came from standard input, this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> field is set to zero.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> relative offset of local header: (4 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This is the offset from the start of the first disk on
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> which this file appears, to where the local header should
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> be found. If an archive is in zip64 format and the value
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in this field is 0xFFFFFFFF, the size will be in the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> corresponding 8 byte zip64 extended information extra field.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file name: (Variable)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The name of the file, with optional relative path.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The path stored should not contain a drive or
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> device letter, or a leading slash. All slashes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> should be forward slashes &amp;#39;/&amp;#39; as opposed to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> backwards slashes &amp;#39;\&amp;#39; for compatibility with Amiga
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and Unix file systems etc. If input came from standard
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> input, there is no file name field. If encrypting
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the central directory and general purpose bit flag 13 is set 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> indicating masking, the file name stored in the Local Header 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> will not be the actual file name. A masking value consisting 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of a unique hexadecimal value will be stored. This value will 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> be sequentially incremented for each file in the archive. See
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the section on the Strong Encryption Specification for details 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> on retrieving the encrypted file name. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> extra field: (Variable)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This is for expansion. If additional information
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> needs to be stored for special needs or for specific 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> platforms, it should be stored here. Earlier versions 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of the software can then safely skip this file, and 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> find the next file or header. This field will be 0 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> length in version 1.0.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> In order to allow different programs and different types
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of information to be stored in the &amp;#39;extra&amp;#39; field in .ZIP
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> files, the following structure should be used for all
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> programs storing data in this field:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> header1+data1 + header2+data2 . . .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Each header should consist of:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Header ID - 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Data Size - 2 bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: all fields stored in Intel low-byte/high-byte order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The Header ID field indicates the type of data that is in
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the following data block.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Header ID&amp;#39;s of 0 thru 31 are reserved for use by PKWARE.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The remaining ID&amp;#39;s can be used by third party vendors for
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> proprietary usage.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The current Header ID mappings defined by PKWARE are:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0001 ZIP64 extended information extra field
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0007 AV Info
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0008 Reserved for future Unicode file name data (PFS)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0009 OS/2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x000a NTFS 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x000c OpenVMS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x000d Unix
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x000e Reserved for file stream and fork descriptors
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x000f Patch Descriptor
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0014 PKCS#7 Store for X.509 Certificates
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0015 X.509 Certificate ID and Signature for 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> individual file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0016 X.509 Certificate ID for Central Directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0017 Strong Encryption Header
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0018 Record Management Controls
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0019 PKCS#7 Encryption Recipient Certificate List
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0065 IBM S/390 (Z390), AS/400 (I400) attributes 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - uncompressed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0066 Reserved for IBM S/390 (Z390), AS/400 (I400) 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> attributes - compressed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Third party mappings commonly used are:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x07c8 Macintosh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x2605 ZipIt Macintosh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x2705 ZipIt Macintosh 1.3.5+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x2805 ZipIt Macintosh 1.3.5+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x334d Info-ZIP Macintosh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x4341 Acorn/SparkFS 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x4453 Windows NT security descriptor (binary ACL)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x4704 VM/CMS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x470f MVS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x4b46 FWKCS MD5 (see below)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x4c41 OS/2 access control list (text ACL)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x4d49 Info-ZIP OpenVMS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x4f4c Xceed original location extra field
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x5356 AOS/VS (ACL)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x5455 extended timestamp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x554e Xceed unicode extra field
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x5855 Info-ZIP Unix (original, also OS/2, NT, etc)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6542 BeOS/BeBox
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x756e ASi Unix
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x7855 Info-ZIP Unix (new)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0xfd4a SMS/QDOS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Detailed descriptions of Extra Fields defined by third 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> party mappings will be documented as information on
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> these data structures is made available to PKWARE. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> PKWARE does not guarantee the accuracy of any published
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> third party data.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The Data Size field indicates the size of the following
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> data block. Programs can use this value to skip to the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> next header block, passing over any data blocks that are
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> not of interest.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: As stated above, the size of the entire .ZIP file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> header, including the file name, comment, and extra
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> field should not exceed 64K in size.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> In case two different programs should appropriate the same
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Header ID value, it is strongly recommended that each
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> program place a unique signature of at least two bytes in
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> size (and preferably 4 bytes or bigger) at the start of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> each data area. Every program should verify that its
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> unique signature is present, in addition to the Header ID
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> value being correct, before assuming that it is a block of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> known type.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -ZIP64 Extended Information Extra Field (0x0001):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The following is the layout of the ZIP64 extended 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> information &amp;#34;extra&amp;#34; block. If one of the size or
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> offset fields in the Local or Central directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> record is too small to hold the required data,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> a ZIP64 extended information record is created.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The order of the fields in the ZIP64 extended 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> information record is fixed, but the fields will
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> only appear if the corresponding Local or Central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory record field is set to 0xFFFF or 0xFFFFFFFF.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: all fields stored in Intel low-byte/high-byte order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (ZIP64) 0x0001 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Size 2 bytes Size of this &amp;#34;extra&amp;#34; block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Original 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Size 8 bytes Original uncompressed file size
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Compressed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Size 8 bytes Size of compressed data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Relative Header
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Offset 8 bytes Offset of local header record
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Disk Start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Number 4 bytes Number of the disk on which
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this file starts 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This entry in the Local header must include BOTH original
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and compressed file sizes.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -OS/2 Extra Field (0x0009):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The following is the layout of the OS/2 attributes &amp;#34;extra&amp;#34; 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> block. (Last Revision 09/05/95)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: all fields stored in Intel low-byte/high-byte order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (OS/2) 0x0009 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size for the following data block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> BSize 4 bytes Uncompressed Block Size
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CType 2 bytes Compression type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> EACRC 4 bytes CRC value for uncompress block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (var) variable Compressed block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The OS/2 extended attribute structure (FEA2LIST) is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compressed and then stored in it&amp;#39;s entirety within this 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> structure. There will only ever be one &amp;#34;block&amp;#34; of data in 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> VarFields[].
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -NTFS Extra Field (0x000a):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The following is the layout of the NTFS attributes 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;extra&amp;#34; block. (Note: At this time the Mtime, Atime
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and Ctime values may be used on any WIN32 system.) 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: all fields stored in Intel low-byte/high-byte order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (NTFS) 0x000a 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size of the total &amp;#34;extra&amp;#34; block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Reserved 4 bytes Reserved for future use
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Tag1 2 bytes NTFS attribute tag value #1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Size1 2 bytes Size of attribute #1, in bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (var.) Size1 Attribute #1 data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TagN 2 bytes NTFS attribute tag value #N
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> SizeN 2 bytes Size of attribute #N, in bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (var.) SizeN Attribute #N data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> For NTFS, values for Tag1 through TagN are as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (currently only one set of attributes is defined for NTFS)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Tag Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0001 2 bytes Tag for attribute #1 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Size1 2 bytes Size of attribute #1, in bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Mtime 8 bytes File last modification time
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Atime 8 bytes File last access time
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Ctime 8 bytes File creation time
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -OpenVMS Extra Field (0x000c):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The following is the layout of the OpenVMS attributes 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;extra&amp;#34; block.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: all fields stored in Intel low-byte/high-byte order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (VMS) 0x000c 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size of the total &amp;#34;extra&amp;#34; block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CRC 4 bytes 32-bit CRC for remainder of the block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Tag1 2 bytes OpenVMS attribute tag value #1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Size1 2 bytes Size of attribute #1, in bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (var.) Size1 Attribute #1 data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TagN 2 bytes OpenVMS attribute tage value #N
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> SizeN 2 bytes Size of attribute #N, in bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (var.) SizeN Attribute #N data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Rules:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1. There will be one or more of attributes present, which 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> will each be preceded by the above TagX &amp;amp; SizeX values. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> These values are identical to the ATR$C_XXXX and 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ATR$S_XXXX constants which are defined in ATR.H under 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> OpenVMS C. Neither of these values will ever be zero.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2. No word alignment or padding is performed.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3. A well-behaved PKZIP/OpenVMS program should never produce
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> more than one sub-block with the same TagX value. Also,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> there will never be more than one &amp;#34;extra&amp;#34; block of type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x000c in a particular directory record.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -UNIX Extra Field (0x000d):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The following is the layout of the Unix &amp;#34;extra&amp;#34; block.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: all fields are stored in Intel low-byte/high-byte 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (UNIX) 0x000d 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size for the following data block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Atime 4 bytes File last access time
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Mtime 4 bytes File last modification time
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Uid 2 bytes File user ID
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Gid 2 bytes File group ID
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (var) variable Variable length data field
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The variable length data field will contain file type 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> specific data. Currently the only values allowed are
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the original &amp;#34;linked to&amp;#34; file names for hard or symbolic 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> links, and the major and minor device node numbers for
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> character and block device nodes. Since device nodes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> cannot be either symbolic or hard links, only one set of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> variable length data is stored. Link files will have the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> name of the original file stored. This name is NOT NULL
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> terminated. Its size can be determined by checking TSize -
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1. Device entries will have eight bytes stored as two 4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> byte entries (in little endian format). The first entry
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> will be the major device number, and the second the minor
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> device number.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -PATCH Descriptor Extra Field (0x000f):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The following is the layout of the Patch Descriptor &amp;#34;extra&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> block.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: all fields stored in Intel low-byte/high-byte order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (Patch) 0x000f 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size of the total &amp;#34;extra&amp;#34; block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Version 2 bytes Version of the descriptor
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Flags 4 bytes Actions and reactions (see below) 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> OldSize 4 bytes Size of the file about to be patched 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> OldCRC 4 bytes 32-bit CRC of the file to be patched 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> NewSize 4 bytes Size of the resulting file 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> NewCRC 4 bytes 32-bit CRC of the resulting file 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Actions and reactions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bits Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ---- ----------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 Use for auto detection
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 Treat as a self-patch
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2-3 RESERVED
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4-5 Action (see below)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6-7 RESERVED
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 8-9 Reaction (see below) to absent file 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 10-11 Reaction (see below) to newer file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 12-13 Reaction (see below) to unknown file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 14-15 RESERVED
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 16-31 RESERVED
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Actions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Action Value
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ------ ----- 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> none 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> add 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> delete 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> patch 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Reactions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Reaction Value
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -------- -----
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ask 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> skip 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ignore 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> fail 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Patch support is provided by PKPatchMaker(tm) technology and is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> covered under U.S. Patents and Patents Pending.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -PKCS#7 Store for X.509 Certificates (0x0014):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This field contains information about each of the certificates 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> files may be signed with. When the Central Directory Encryption 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> feature is enabled for a ZIP file, this record will appear in 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the Archive Extra Data Record, otherwise it will appear in the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> first central directory record and will be ignored in any 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> other record.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: all fields stored in Intel low-byte/high-byte order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (Store) 0x0014 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size of the store data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TData TSize Data about the store
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -X.509 Certificate ID and Signature for individual file (0x0015):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This field contains the information about which certificate in 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the PKCS#7 store was used to sign a particular file. It also 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> contains the signature data. This field can appear multiple 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> times, but can only appear once per certificate.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: all fields stored in Intel low-byte/high-byte order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (CID) 0x0015 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size of data that follows
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TData TSize Signature Data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -X.509 Certificate ID and Signature for central directory (0x0016):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This field contains the information about which certificate in 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the PKCS#7 store was used to sign the central directory structure.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> When the Central Directory Encryption feature is enabled for a 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ZIP file, this record will appear in the Archive Extra Data Record, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> otherwise it will appear in the first central directory record.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: all fields stored in Intel low-byte/high-byte order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (CDID) 0x0016 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size of data that follows
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TData TSize Data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -Strong Encryption Header (0x0017) (EFS):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0017 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size of data that follows
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Format 2 bytes Format definition for this record
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AlgID 2 bytes Encryption algorithm identifier
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bitlen 2 bytes Bit length of encryption key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Flags 2 bytes Processing flags
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CertData TSize-8 Certificate decryption extra field data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (refer to the explanation for CertData
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in the section describing the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Certificate Processing Method under 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the Strong Encryption Specification)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -Record Management Controls (0x0018):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(Rec-CTL) 0x0018 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CSize 2 bytes Size of total extra block data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Tag1 2 bytes Record control attribute 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Size1 2 bytes Size of attribute 1, in bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Data1 Size1 Attribute 1 data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TagN 2 bytes Record control attribute N
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> SizeN 2 bytes Size of attribute N, in bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> DataN SizeN Attribute N data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -PKCS#7 Encryption Recipient Certificate List (0x0019): (EFS)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This field contains the information about each of the certificates
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> that files may be encrypted with. This field should only appear 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in the archive extra data record. This field is not required and 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> serves only to aide archive modifications by preserving public 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> encryption data. Individual security requirements may dictate 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> that this data be omitted to deter information exposure.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: all fields stored in Intel low-byte/high-byte order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (CStore) 0x0019 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size of the store data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TData TSize Data about the store
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TData:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Version 2 bytes Format version number - must 0x0001 at this time
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CStore (var) PKCS#7 data blob
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -MVS Extra Field (0x0065):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The following is the layout of the MVS &amp;#34;extra&amp;#34; block.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: Some fields are stored in Big Endian format.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> All text is in EBCDIC format unless otherwise specified.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (MVS) 0x0065 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size for the following data block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ID 4 bytes EBCDIC &amp;#34;Z390&amp;#34; 0xE9F3F9F0 or
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;T4MV&amp;#34; for TargetFour
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (var) TSize-4 Attribute data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -OS/400 Extra Field (0x0065):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The following is the layout of the OS/400 &amp;#34;extra&amp;#34; block.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Note: Some fields are stored in Big Endian format.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> All text is in EBCDIC format unless otherwise specified.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (OS400) 0x0065 2 bytes Tag for this &amp;#34;extra&amp;#34; block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize 2 bytes Size for the following data block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ID 4 bytes EBCDIC &amp;#34;I400&amp;#34; 0xC9F4F0F0 or
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;T4MV&amp;#34; for TargetFour
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (var) TSize-4 Attribute data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Third-party Mappings:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -ZipIt Macintosh Extra Field (long) (0x2605):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The following is the layout of the ZipIt extra block 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for Macintosh. The local-header and central-header versions 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> are identical. This block must be present if the file is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> stored MacBinary-encoded and it should not be used if the file 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is not stored MacBinary-encoded.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (Mac2) 0x2605 Short tag for this extra block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize Short total data size for this block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;ZPIT&amp;#34; beLong extra-field signature
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> FnLen Byte length of FileName
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> FileName variable full Macintosh filename
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> FileType Byte[4] four-byte Mac file type string
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Creator Byte[4] four-byte Mac creator string
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -ZipIt Macintosh Extra Field (short, for files) (0x2705):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The following is the layout of a shortened variant of the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ZipIt extra block for Macintosh (without &amp;#34;full name&amp;#34; entry).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This variant is used by ZipIt 1.3.5 and newer for entries of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> files (not directories) that do not have a MacBinary encoded
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file. The local-header and central-header versions are identical.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (Mac2b) 0x2705 Short tag for this extra block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize Short total data size for this block (12)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;ZPIT&amp;#34; beLong extra-field signature
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> FileType Byte[4] four-byte Mac file type string
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Creator Byte[4] four-byte Mac creator string
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> fdFlags beShort attributes from FInfo.frFlags,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> may be omitted
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0000 beShort reserved, may be omitted
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -ZipIt Macintosh Extra Field (short, for directories) (0x2805):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The following is the layout of a shortened variant of the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ZipIt extra block for Macintosh used only for directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> entries. This variant is used by ZipIt 1.3.5 and newer to 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> save some optional Mac-specific information about directories.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The local-header and central-header versions are identical.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (Mac2c) 0x2805 Short tag for this extra block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> TSize Short total data size for this block (12)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;ZPIT&amp;#34; beLong extra-field signature
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> frFlags beShort attributes from DInfo.frFlags, may
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> be omitted
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> View beShort ZipIt view flag, may be omitted
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The View field specifies ZipIt-internal settings as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bits of the Flags:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> bit 0 if set, the folder is shown expanded (open)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> when the archive contents are viewed in ZipIt.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> bits 1-15 reserved, zero;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -FWKCS MD5 Extra Field (0x4b46):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The FWKCS Contents_Signature System, used in
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> automatically identifying files independent of file name,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> optionally adds and uses an extra field to support the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rapid creation of an enhanced contents_signature:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Header ID = 0x4b46
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Data Size = 0x0013
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Preface = &amp;#39;M&amp;#39;,&amp;#39;D&amp;#39;,&amp;#39;5&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> followed by 16 bytes containing the uncompressed file&amp;#39;s
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 128_bit MD5 hash(1), low byte first.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> When FWKCS revises a .ZIP file central directory to add
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this extra field for a file, it also replaces the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> central directory entry for that file&amp;#39;s uncompressed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file length with a measured value.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> FWKCS provides an option to strip this extra field, if
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> present, from a .ZIP file central directory. In adding
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this extra field, FWKCS preserves .ZIP file Authenticity
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Verification; if stripping this extra field, FWKCS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> preserves all versions of AV through PKZIP version 2.04g.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> FWKCS, and FWKCS Contents_Signature System, are
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> trademarks of Frederick W. Kantor.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (1) R. Rivest, RFC1321.TXT, MIT Laboratory for Computer
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Science and RSA Data Security, Inc., April 1992.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ll.76-77: &amp;#34;The MD5 algorithm is being placed in the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> public domain for review and possible adoption as a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> standard.&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file comment: (Variable)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The comment for this file.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> number of this disk: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The number of this disk, which contains central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory end record. If an archive is in zip64 format
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and the value in this field is 0xFFFF, the size will 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> be in the corresponding 4 byte zip64 end of central 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory field.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> number of the disk with the start of the central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The number of the disk on which the central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory starts. If an archive is in zip64 format
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and the value in this field is 0xFFFF, the size will 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> be in the corresponding 4 byte zip64 end of central 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory field.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> total number of entries in the central dir on 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this disk: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The number of central directory entries on this disk.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> If an archive is in zip64 format and the value in 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this field is 0xFFFF, the size will be in the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> corresponding 8 byte zip64 end of central 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory field.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> total number of entries in the central dir: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The total number of files in the .ZIP file. If an 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> archive is in zip64 format and the value in this field
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is 0xFFFF, the size will be in the corresponding 8 byte 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> zip64 end of central directory field.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> size of the central directory: (4 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The size (in bytes) of the entire central directory.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> If an archive is in zip64 format and the value in 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this field is 0xFFFFFFFF, the size will be in the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> corresponding 8 byte zip64 end of central 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory field.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> offset of start of central directory with respect to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the starting disk number: (4 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Offset of the start of the central directory on the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> disk on which the central directory starts. If an 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> archive is in zip64 format and the value in this 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> field is 0xFFFFFFFF, the size will be in the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> corresponding 8 byte zip64 end of central 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> directory field.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .ZIP file comment length: (2 bytes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The length of the comment for this .ZIP file.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> .ZIP file comment: (Variable)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The comment for this .ZIP file. ZIP file comment data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is stored unsecured. No encryption or data authentication
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is applied to this area at this time. Confidential information
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> should not be stored in this section.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> zip64 extensible data sector (variable size)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (currently reserved for use by PKWARE)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> K. General notes:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1) All fields unless otherwise noted are unsigned and stored
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in Intel low-byte:high-byte, low-word:high-word order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2) String fields are not null terminated, since the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> length is given explicitly.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3) Local headers should not span disk boundaries. Also, even
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> though the central directory can span disk boundaries, no
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> single record in the central directory should be split
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> across disks.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4) The entries in the central directory may not necessarily
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> be in the same order that files appear in the .ZIP file.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5) Spanned/Split archives created using PKZIP for Windows
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (V2.50 or greater), PKZIP Command Line (V2.50 or greater),
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> or PKZIP Explorer will include a special spanning 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> signature as the first 4 bytes of the first segment of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the archive. This signature (0x08074b50) will be 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> followed immediately by the local header signature for
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the first file in the archive. A special spanning
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> marker may also appear in spanned/split archives if the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> spanning or splitting process starts but only requires
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> one segment. In this case the 0x08074b50 signature
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> will be replaced with the temporary spanning marker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> signature of 0x30304b50. Spanned/split archives
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> created with this special signature are compatible with
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> all versions of PKZIP from PKWARE. Split archives can
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> only be uncompressed by other versions of PKZIP that
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> know how to create a split archive.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6) If one of the fields in the end of central directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> record is too small to hold required data, the field
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> should be set to -1 (0xFFFF or 0xFFFFFFFF) and the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Zip64 format record should be created.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7) The end of central directory record and the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Zip64 end of central directory locator record must
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> reside on the same disk when splitting or spanning
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> an archive.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">V. UnShrinking - Method 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Shrinking is a Dynamic Ziv-Lempel-Welch compression algorithm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">with partial clearing. The initial code size is 9 bits, and
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the maximum code size is 13 bits. Shrinking differs from
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">conventional Dynamic Ziv-Lempel-Welch implementations in several
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">respects:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1) The code size is controlled by the compressor, and is not
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> automatically increased when codes larger than the current
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> code size are created (but not necessarily used). When
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the decompressor encounters the code sequence 256
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (decimal) followed by 1, it should increase the code size
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read from the input stream to the next bit size. No
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> blocking of the codes is performed, so the next code at
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the increased size should be read from the input stream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> immediately after where the previous code at the smaller
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> bit size was read. Again, the decompressor should not
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> increase the code size used until the sequence 256,1 is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> encountered.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2) When the table becomes full, total clearing is not
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> performed. Rather, when the compressor emits the code
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sequence 256,2 (decimal), the decompressor should clear
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> all leaf nodes from the Ziv-Lempel tree, and continue to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> use the current code size. The nodes that are cleared
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> from the Ziv-Lempel tree are then re-used, with the lowest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> code value re-used first, and the highest code value
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> re-used last. The compressor can emit the sequence 256,2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at any time.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">VI. Expanding - Methods 2-5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Reducing algorithm is actually a combination of two
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">distinct algorithms. The first algorithm compresses repeated
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">byte sequences, and the second algorithm takes the compressed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">stream from the first algorithm and applies a probabilistic
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">compression method.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The probabilistic compression stores an array of &amp;#39;follower
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sets&amp;#39; S(j), for j=0 to 255, corresponding to each possible
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ASCII character. Each set contains between 0 and 32
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">characters, to be denoted as S(j)[0],...,S(j)[m], where m&amp;lt;32.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The sets are stored at the beginning of the data area for a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Reduced file, in reverse order, with S(255) first, and S(0)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">last.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The sets are encoded as { N(j), S(j)[0],...,S(j)[N(j)-1] },
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">where N(j) is the size of set S(j). N(j) can be 0, in which
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">case the follower set for S(j) is empty. Each N(j) value is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encoded in 6 bits, followed by N(j) eight bit character values
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">corresponding to S(j)[0] to S(j)[N(j)-1] respectively. If
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">N(j) is 0, then no values for S(j) are stored, and the value
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">for N(j-1) immediately follows.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Immediately after the follower sets, is the compressed data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">stream. The compressed data stream can be interpreted for the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">probabilistic decompression as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">let Last-Character &amp;lt;- 0.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">loop until done
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if the follower set S(Last-Character) is empty then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read 8 bits from the input stream, and copy this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> value to the output stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> otherwise if the follower set S(Last-Character) is non-empty then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read 1 bit from the input stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if this bit is not zero then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read 8 bits from the input stream, and copy this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> value to the output stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> otherwise if this bit is zero then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read B(N(Last-Character)) bits from the input
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> stream, and assign this value to I.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Copy the value of S(Last-Character)[I] to the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> output stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> assign the last value placed on the output stream to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Last-Character.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">end loop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">B(N(j)) is defined as the minimal number of bits required to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encode the value N(j)-1.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The decompressed stream from above can then be expanded to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">re-create the original file as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">let State &amp;lt;- 0.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">loop until done
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read 8 bits from the input stream into C.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> case State of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0: if C is not equal to DLE (144 decimal) then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> copy C to the output stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> otherwise if C is equal to DLE then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> let State &amp;lt;- 1.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1: if C is non-zero then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> let V &amp;lt;- C.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> let Len &amp;lt;- L(V)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> let State &amp;lt;- F(Len).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> otherwise if C is zero then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> copy the value 144 (decimal) to the output stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> let State &amp;lt;- 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2: let Len &amp;lt;- Len + C
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> let State &amp;lt;- 3.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3: move backwards D(V,C) bytes in the output stream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (if this position is before the start of the output
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> stream, then assume that all the data before the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> start of the output stream is filled with zeros).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> copy Len+3 bytes from this position to the output stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> let State &amp;lt;- 0.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> end case
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">end loop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The functions F,L, and D are dependent on the &amp;#39;compression
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">factor&amp;#39;, 1 through 4, and are defined as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">For compression factor 1:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> L(X) equals the lower 7 bits of X.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> F(X) equals 2 if X equals 127 otherwise F(X) equals 3.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> D(X,Y) equals the (upper 1 bit of X) * 256 + Y + 1.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">For compression factor 2:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> L(X) equals the lower 6 bits of X.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> F(X) equals 2 if X equals 63 otherwise F(X) equals 3.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> D(X,Y) equals the (upper 2 bits of X) * 256 + Y + 1.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">For compression factor 3:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> L(X) equals the lower 5 bits of X.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> F(X) equals 2 if X equals 31 otherwise F(X) equals 3.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> D(X,Y) equals the (upper 3 bits of X) * 256 + Y + 1.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">For compression factor 4:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> L(X) equals the lower 4 bits of X.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> F(X) equals 2 if X equals 15 otherwise F(X) equals 3.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> D(X,Y) equals the (upper 4 bits of X) * 256 + Y + 1.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">VII. Imploding - Method 6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Imploding algorithm is actually a combination of two distinct
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">algorithms. The first algorithm compresses repeated byte
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sequences using a sliding dictionary. The second algorithm is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">used to compress the encoding of the sliding dictionary output,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">using multiple Shannon-Fano trees.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Imploding algorithm can use a 4K or 8K sliding dictionary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">size. The dictionary size used can be determined by bit 1 in the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">general purpose flag word; a 0 bit indicates a 4K dictionary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">while a 1 bit indicates an 8K dictionary.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Shannon-Fano trees are stored at the start of the compressed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">file. The number of trees stored is defined by bit 2 in the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">general purpose flag word; a 0 bit indicates two trees stored, a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1 bit indicates three trees are stored. If 3 trees are stored,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the first Shannon-Fano tree represents the encoding of the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Literal characters, the second tree represents the encoding of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the Length information, the third represents the encoding of the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Distance information. When 2 Shannon-Fano trees are stored, the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Length tree is stored first, followed by the Distance tree.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Literal Shannon-Fano tree, if present is used to represent
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the entire ASCII character set, and contains 256 values. This
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tree is used to compress any data not compressed by the sliding
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">dictionary algorithm. When this tree is present, the Minimum
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Match Length for the sliding dictionary is 3. If this tree is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">not present, the Minimum Match Length is 2.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Length Shannon-Fano tree is used to compress the Length part
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">of the (length,distance) pairs from the sliding dictionary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">output. The Length tree contains 64 values, ranging from the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Minimum Match Length, to 63 plus the Minimum Match Length.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Distance Shannon-Fano tree is used to compress the Distance
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">part of the (length,distance) pairs from the sliding dictionary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">output. The Distance tree contains 64 values, ranging from 0 to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">63, representing the upper 6 bits of the distance value. The
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">distance values themselves will be between 0 and the sliding
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">dictionary size, either 4K or 8K.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Shannon-Fano trees themselves are stored in a compressed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">format. The first byte of the tree data represents the number of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bytes of data representing the (compressed) Shannon-Fano tree
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">minus 1. The remaining bytes represent the Shannon-Fano tree
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data encoded as:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> High 4 bits: Number of values at this bit length + 1. (1 - 16)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Low 4 bits: Bit Length needed to represent value + 1. (1 - 16)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Shannon-Fano codes can be constructed from the bit lengths
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">using the following algorithm:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1) Sort the Bit Lengths in ascending order, while retaining the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> order of the original lengths stored in the file.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2) Generate the Shannon-Fano trees:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Code &amp;lt;- 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CodeIncrement &amp;lt;- 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> LastBitLength &amp;lt;- 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> i &amp;lt;- number of Shannon-Fano codes - 1 (either 255 or 63)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> loop while i &amp;gt;= 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Code = Code + CodeIncrement
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if BitLength(i) &amp;lt;&amp;gt; LastBitLength then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> LastBitLength=BitLength(i)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CodeIncrement = 1 shifted left (16 - LastBitLength)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ShannonCode(i) = Code
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> i &amp;lt;- i - 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> end loop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3) Reverse the order of all the bits in the above ShannonCode()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> vector, so that the most significant bit becomes the least
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> significant bit. For example, the value 0x1234 (hex) would
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> become 0x2C48 (hex).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4) Restore the order of Shannon-Fano codes as originally stored
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> within the file.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Example:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This example will show the encoding of a Shannon-Fano tree
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of size 8. Notice that the actual Shannon-Fano trees used
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for Imploding are either 64 or 256 entries in size.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Example: 0x02, 0x42, 0x01, 0x13
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The first byte indicates 3 values in this table. Decoding the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> bytes:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x42 = 5 codes of 3 bits long
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x01 = 1 code of 2 bits long
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x13 = 2 codes of 4 bits long
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This would generate the original bit length array of:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (3, 3, 3, 3, 3, 2, 4, 4)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> There are 8 codes in this table for the values 0 thru 7. Using 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the algorithm to obtain the Shannon-Fano codes produces:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Reversed Order Original
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Val Sorted Constructed Code Value Restored Length
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--- ------ ----------------- -------- -------- ------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0: 2 1100000000000000 11 101 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1: 3 1010000000000000 101 001 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2: 3 1000000000000000 001 110 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3: 3 0110000000000000 110 010 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4: 3 0100000000000000 010 100 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5: 3 0010000000000000 100 11 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6: 4 0001000000000000 1000 1000 4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">7: 4 0000000000000000 0000 0000 4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The values in the Val, Order Restored and Original Length columns
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">now represent the Shannon-Fano encoding tree that can be used for
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">decoding the Shannon-Fano encoded data. How to parse the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">variable length Shannon-Fano values from the data stream is beyond
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the scope of this document. (See the references listed at the end of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">this document for more information.) However, traditional decoding
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">schemes used for Huffman variable length decoding, such as the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Greenlaw algorithm, can be successfully applied.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The compressed data stream begins immediately after the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">compressed Shannon-Fano data. The compressed data stream can be
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">interpreted as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">loop until done
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read 1 bit from input stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if this bit is non-zero then (encoded data is literal data)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if Literal Shannon-Fano tree is present
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read and decode character using Literal Shannon-Fano tree.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> otherwise
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read 8 bits from input stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> copy character to the output stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> otherwise (encoded data is sliding dictionary match)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if 8K dictionary size
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read 7 bits for offset Distance (lower 7 bits of offset).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> otherwise
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read 6 bits for offset Distance (lower 6 bits of offset).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> using the Distance Shannon-Fano tree, read and decode the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> upper 6 bits of the Distance value.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> using the Length Shannon-Fano tree, read and decode
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the Length value.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Length &amp;lt;- Length + Minimum Match Length
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if Length = 63 + Minimum Match Length
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read 8 bits from the input stream,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> add this value to Length.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> move backwards Distance+1 bytes in the output stream, and
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> copy Length characters from this position to the output
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> stream. (if this position is before the start of the output
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> stream, then assume that all the data before the start of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the output stream is filled with zeros).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">end loop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">VIII. Tokenizing - Method 7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">This method is not used by PKZIP.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IX. Deflating - Method 8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Deflate algorithm is similar to the Implode algorithm using
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a sliding dictionary of up to 32K with secondary compression
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">from Huffman/Shannon-Fano codes.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The compressed data is stored in blocks with a header describing
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the block and the Huffman codes used in the data block. The header
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">format is as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bit 0: Last Block bit This bit is set to 1 if this is the last
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compressed block in the data.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bits 1-2: Block type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 00 (0) - Block is stored - All stored data is byte aligned.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Skip bits until next byte, then next word = block 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> length, followed by the ones compliment of the block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> length word. Remaining data in block is the stored 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> data.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 01 (1) - Use fixed Huffman codes for literal and distance codes.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Lit Code Bits Dist Code Bits
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> --------- ---- --------- ----
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 - 143 8 0 - 31 5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 144 - 255 9
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 256 - 279 7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 280 - 287 8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Literal codes 286-287 and distance codes 30-31 are 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> never used but participate in the huffman construction.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 10 (2) - Dynamic Huffman codes. (See expanding Huffman codes)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 11 (3) - Reserved - Flag a &amp;#34;Error in compressed data&amp;#34; if seen.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Expanding Huffman Codes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-----------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">If the data block is stored with dynamic Huffman codes, the Huffman
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">codes are sent in the following compressed format:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 Bits: # of Literal codes sent - 256 (256 - 286)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> All other codes are never sent.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 Bits: # of Dist codes - 1 (1 - 32)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 Bits: # of Bit Length codes - 3 (3 - 19)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Huffman codes are sent as bit lengths and the codes are built as
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">described in the implode algorithm. The bit lengths themselves are
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">compressed with Huffman codes. There are 19 bit length codes:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 - 15: Represent bit lengths of 0 - 15
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 16: Copy the previous bit length 3 - 6 times.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The next 2 bits indicate repeat length (0 = 3, ... ,3 = 6)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Example: Codes 8, 16 (+2 bits 11), 16 (+2 bits 10) will
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> expand to 12 bit lengths of 8 (1 + 6 + 5)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 17: Repeat a bit length of 0 for 3 - 10 times. (3 bits of length)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 18: Repeat a bit length of 0 for 11 - 138 times (7 bits of length)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The lengths of the bit length codes are sent packed 3 bits per value
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(0 - 7) in the following order:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Huffman codes should be built as described in the Implode algorithm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">except codes are assigned starting at the shortest bit length, i.e. the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">shortest code should be all 0&amp;#39;s rather than all 1&amp;#39;s. Also, codes with
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a bit length of zero do not participate in the tree construction. The
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">codes are then used to decode the bit lengths for the literal and 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">distance tables.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The bit lengths for the literal tables are sent first with the number
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">of entries sent described by the 5 bits sent earlier. There are up
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to 286 literal characters; the first 256 represent the respective 8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bit character, code 256 represents the End-Of-Block code, the remaining
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">29 codes represent copy lengths of 3 thru 258. There are up to 30
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">distance codes representing distances from 1 thru 32k as described
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">below.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Length Codes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Extra Extra Extra Extra
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Code Bits Length Code Bits Lengths Code Bits Lengths Code Bits Length(s)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ---- ---- ------ ---- ---- ------- ---- ---- ------- ---- ---- ---------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 257 0 3 265 1 11,12 273 3 35-42 281 5 131-162
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 258 0 4 266 1 13,14 274 3 43-50 282 5 163-194
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 259 0 5 267 1 15,16 275 3 51-58 283 5 195-226
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 260 0 6 268 1 17,18 276 3 59-66 284 5 227-257
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 261 0 7 269 2 19-22 277 4 67-82 285 0 258
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 262 0 8 270 2 23-26 278 4 83-98
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 263 0 9 271 2 27-30 279 4 99-114
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 264 0 10 272 2 31-34 280 4 115-130
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Distance Codes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> --------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Extra Extra Extra Extra
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Code Bits Dist Code Bits Dist Code Bits Distance Code Bits Distance
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ---- ---- ---- ---- ---- ------ ---- ---- -------- ---- ---- --------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0 0 1 8 3 17-24 16 7 257-384 24 11 4097-6144
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 0 2 9 3 25-32 17 7 385-512 25 11 6145-8192
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 0 3 10 4 33-48 18 8 513-768 26 12 8193-12288
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3 0 4 11 4 49-64 19 8 769-1024 27 12 12289-16384
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 1 5,6 12 5 65-96 20 9 1025-1536 28 13 16385-24576
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 5 1 7,8 13 5 97-128 21 9 1537-2048 29 13 24577-32768
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 2 9-12 14 6 129-192 22 10 2049-3072
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 7 2 13-16 15 6 193-256 23 10 3073-4096
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The compressed data stream begins immediately after the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">compressed header data. The compressed data stream can be
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">interpreted as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">do
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read header from input stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if stored block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> skip bits until byte aligned
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read count and 1&amp;#39;s compliment of count
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> copy count bytes data block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> otherwise
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> loop until end of block code sent
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> decode literal character from input stream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if literal &amp;lt; 256
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> copy character to the output stream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> otherwise
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if literal = end of block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> break from loop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> otherwise
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> decode distance from input stream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> move backwards distance bytes in the output stream, and
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> copy length characters from this position to the output
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> stream.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> end loop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">while not last block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">if data descriptor exists
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> skip bits until byte aligned
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read crc and sizes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">endif
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">X. Enhanced Deflating - Method 9
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Enhanced Deflating algorithm is similar to Deflate but
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">uses a sliding dictionary of up to 64K. Deflate64(tm) is supported
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">by the Deflate extractor. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">XI. BZIP2 - Method 12
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">BZIP2 is an open-source data compression algorithm developed by 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Julian Seward. Information and source code for this algorithm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">can be found on the internet.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">XII. Traditional PKWARE Encryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">----------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The following information discusses the decryption steps
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">required to support traditional PKWARE encryption. This
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">form of encryption is considered weak by today&amp;#39;s standards
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">and its use is recommended only for situations with
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">low security needs or for compatibility with older .ZIP 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">applications.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">XIII. Decryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">----------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The encryption used in PKZIP was generously supplied by Roger
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Schlafly. PKWARE is grateful to Mr. Schlafly for his expert
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">help and advice in the field of data encryption.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PKZIP encrypts the compressed data stream. Encrypted files must
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">be decrypted before they can be extracted.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Each encrypted file has an extra 12 bytes stored at the start of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the data area defining the encryption header for that file. The
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encryption header is originally set to random values, and then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">itself encrypted, using three, 32-bit keys. The key values are
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">initialized using the supplied encryption password. After each byte
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">is encrypted, the keys are then updated using pseudo-random number
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">generation techniques in combination with the same CRC-32 algorithm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">used in PKZIP and described elsewhere in this document.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The following is the basic steps required to decrypt a file:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1) Initialize the three 32-bit keys with the password.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2) Read and decrypt the 12-byte encryption header, further
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> initializing the encryption keys.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3) Read and decrypt the compressed data stream using the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> encryption keys.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Step 1 - Initializing the encryption keys
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-----------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Key(0) &amp;lt;- 305419896
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Key(1) &amp;lt;- 591751049
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Key(2) &amp;lt;- 878082192
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">loop for i &amp;lt;- 0 to length(password)-1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> update_keys(password(i))
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">end loop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Where update_keys() is defined as:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">update_keys(char):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Key(0) &amp;lt;- crc32(key(0),char)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Key(1) &amp;lt;- Key(1) + (Key(0) &amp;amp; 000000ffH)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Key(1) &amp;lt;- Key(1) * 134775813 + 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Key(2) &amp;lt;- crc32(key(2),key(1) &amp;gt;&amp;gt; 24)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">end update_keys
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Where crc32(old_crc,char) is a routine that given a CRC value and a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">character, returns an updated CRC value after applying the CRC-32
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">algorithm described elsewhere in this document.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Step 2 - Decrypting the encryption header
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-----------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The purpose of this step is to further initialize the encryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">keys, based on random data, to render a plaintext attack on the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data ineffective.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Read the 12-byte encryption header into Buffer, in locations
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Buffer(0) thru Buffer(11).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">loop for i &amp;lt;- 0 to 11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> C &amp;lt;- buffer(i) ^ decrypt_byte()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> update_keys(C)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> buffer(i) &amp;lt;- C
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">end loop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Where decrypt_byte() is defined as:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">unsigned char decrypt_byte()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> local unsigned short temp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> temp &amp;lt;- Key(2) | 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> decrypt_byte &amp;lt;- (temp * (temp ^ 1)) &amp;gt;&amp;gt; 8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">end decrypt_byte
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">After the header is decrypted, the last 1 or 2 bytes in Buffer
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">should be the high-order word/byte of the CRC for the file being
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">decrypted, stored in Intel low-byte/high-byte order. Versions of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PKZIP prior to 2.0 used a 2 byte CRC check; a 1 byte CRC check is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">used on versions after 2.0. This can be used to test if the password
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">supplied is correct or not.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Step 3 - Decrypting the compressed data stream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">----------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The compressed data stream can be decrypted as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">loop until done
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> read a character into C
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Temp &amp;lt;- C ^ decrypt_byte()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> update_keys(temp)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> output Temp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">end loop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">XIV. Strong Encryption Specification (EFS)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Version 5.x of this specification introduced support for strong 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encryption algorithms. These algorithms can be used with either 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a password or an X.509v3 digital certificate to encrypt each file. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">This format specification supports either password or certificate 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">based encryption to meet the security needs of today, to enable 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">interoperability between users within both PKI and non-PKI 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">environments, and to ensure interoperability between different 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">computing platforms that are running a ZIP program. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Password based encryption is the most common form of encryption 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">people are familiar with. However, inherent weaknesses with 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">passwords (e.g. susceptibility to dictionary/brute force attack) 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">as well as password management and support issues make certificate 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">based encryption a more secure and scalable option. Industry 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">efforts and support are defining and moving towards more advanced 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">security solutions built around X.509v3 digital certificates and 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Public Key Infrastructures(PKI) because of the greater scalability, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">administrative options, and more robust security over traditional 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">password based encryption. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Most standard encryption algorithms are supported with this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">specification. Reference implementations for many of these 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">algorithms are available from either commercial or open source 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">distributors. Readily available cryptographic toolkits make
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">implementation of the encryption features straight-forward. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">This document is not intended to provide a treatise on data 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encryption principles or theory. Its purpose is to document the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data structures required for implementing interoperable data 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encryption within the .ZIP format. It is strongly recommended that 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">you have a good understanding of data encryption before reading 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">further.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The algorithms introduced in Version 5.0 of this specification 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">include:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> RC2 40 bit, 64 bit, and 128 bit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> RC4 40 bit, 64 bit, and 128 bit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> DES
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 3DES 112 bit and 168 bit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Version 5.1 adds support for the following:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AES 128 bit, 192 bit, and 256 bit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Version 6.1 introduces encryption data changes to support 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">interoperability with SmartCard and USB Token certificate storage 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">methods which do not support the OAEP strengthening standard.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Version 6.2 introduces support for encrypting metadata by compressing 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">and encrypting the central directory data structure to reduce information 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">leakage. Information leakage can occur in legacy ZIP applications 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">through exposure of information about a file even though that file is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">stored encrypted. The information exposed consists of file 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">characteristics stored within the records and fields defined by this 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">specification. This includes data such as a files name, its original 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">size, timestamp and CRC32 value. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Central Directory Encryption provides greater protection against 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">information leakage by encrypting the Central Directory structure and 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">by masking key values that are replicated in the unencrypted Local 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Header. ZIP compatible programs that cannot interpret an encrypted 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Central Directory structure cannot rely on the data in the corresponding 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Local Header for decompression information. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Extra Field records that may contain information about a file that should 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">not be exposed should not be stored in the Local Header and should only 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">be written to the Central Directory where they can be encrypted. This 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">design currently does not support streaming. Information in the End of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Central Directory record, the ZIP64 End of Central Directory Locator, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">and the ZIP64 End of Central Directory record are not encrypted. Access 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to view data on files within a ZIP file with an encrypted Central Directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">requires the appropriate password or private key for decryption prior to 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">viewing any files, or any information about the files, in the archive. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Older ZIP compatible programs not familiar with the Central Directory 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Encryption feature will no longer be able to recognize the Central 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Directory and may assume the ZIP file is corrupt. Programs that 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">attempt streaming access using Local Headers will see invalid 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">information for each file. Central Directory Encryption need not be 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">used for every ZIP file. Its use is recommended for greater security. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ZIP files not using Central Directory Encryption should operate as 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">in the past. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The details of the strong encryption specification for certificates 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">remain under development as design and testing issues are worked out 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">for the range of algorithms, encryption methods, certificate processing 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">and cross-platform support necessary to meet the advanced security needs 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">of .ZIP file users today and in the future. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">This feature specification is intended to support basic encryption needs 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">of today, such as password support. However this specification is also 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">designed to lay the foundation for future advanced security needs.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Encryption provides data confidentiality and privacy. It is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">recommended that you combine X.509 digital signing with encryption 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to add authentication and non-repudiation.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Single Password Symmetric Encryption Method:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Single Password Symmetric Encryption Method using strong 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encryption algorithms operates similarly to the traditional 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PKWARE encryption defined in this format. Additional data 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">structures are added to support the processing needs of the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">strong algorithms.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Strong Encryption data structures are:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. General Purpose Bits - Bits 0 and 6 of the General Purpose bit 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">flag in both local and central header records. Both bits set 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">indicates strong encryption. Bit 13, when set indicates the Central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Directory is encrypted and that selected fields in the Local Header
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">are masked to hide their actual value.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. Extra Field 0x0017 in central header only.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Fields to consider in this record are:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Format - the data format identifier for this record. The only
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> value allowed at this time is the integer value 2.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AlgId - integer identifier of the encryption algorithm from the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> following range
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6601 - DES
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6602 - RC2 (version needed to extract &amp;lt; 5.2)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6603 - 3DES 168
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6609 - 3DES 112
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x660E - AES 128 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x660F - AES 192 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6610 - AES 256 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6702 - RC2 (version needed to extract &amp;gt;= 5.2)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6801 - RC4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0xFFFF - Unknown algorithm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bitlen - Explicit bit length of key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 40
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 56
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 112
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 128
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 168
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 192
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 256
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Flags - Processing flags needed for decryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0001 - Password is required to decrypt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0002 - Certificates only
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0003 - Password or certificate required to decrypt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Values &amp;gt; 0x0003 reserved for certificate processing
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. Decryption header record preceding compressed file data.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -Decryption Header:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> IVSize 2 bytes Size of initialization vector (IV)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> IVData IVSize Initialization vector for this file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Size 4 bytes Size of remaining decryption header data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Format 2 bytes Format definition for this record
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AlgID 2 bytes Encryption algorithm identifier
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bitlen 2 bytes Bit length of encryption key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Flags 2 bytes Processing flags
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ErdSize 2 bytes Size of Encrypted Random Data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ErdData ErdSize Encrypted Random Data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Reserved1 4 bytes Reserved certificate processing data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Reserved2 (var) Reserved for certificate processing data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> VSize 2 bytes Size of password validation data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> VData VSize-4 Password validation data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> VCRC32 4 bytes Standard ZIP CRC32 of password validation data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> IVData - The size of the IV should match the algorithm block size.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The IVData can be completely random data. If the size of
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the randomly generated data does not match the block size
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> it should be complemented with zero&amp;#39;s or truncated as
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> necessary. If IVSize is 0,then IV = CRC32 + Uncompressed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> File Size (as a 64 bit little-endian, unsigned integer value).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Format - the data format identifier for this record. The only
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> value allowed at this time is the integer value 3.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AlgId - integer identifier of the encryption algorithm from the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> following range
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6601 - DES
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6602 - RC2 (version needed to extract &amp;lt; 5.2)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6603 - 3DES 168
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6609 - 3DES 112
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x660E - AES 128 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x660F - AES 192 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6610 - AES 256 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6702 - RC2 (version needed to extract &amp;gt;= 5.2)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x6801 - RC4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0xFFFF - Unknown algorithm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Bitlen - Explicit bit length of key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 40
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 56
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 112
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 128
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 168
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 192
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 256
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Flags - Processing flags needed for decryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0001 - Password is required to decrypt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0002 - Certificates only
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0003 - Password or certificate required to decrypt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Values &amp;gt; 0x0003 reserved for certificate processing
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ErdData - Encrypted random data is used to generate a file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> session key for encrypting each file. SHA1 is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> used to calculate hash data used to derive keys.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> File session keys are derived from a master session
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> key generated from the user-supplied password.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> If the Flags field in the decryption header contains 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the value 0x4000, then the ErdData field must be 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> decrypted using 3DES.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Reserved1 - Reserved for certificate processing, if value is
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> zero, then Reserved2 data is absent. See the explanation
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> under the Certificate Processing Method for details on
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this data structure.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Reserved2 - If present, the size of the Reserved2 data structure 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is located by skipping the first 4 bytes of this field 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> and using the next 2 bytes as the remaining size. See
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the explanation under the Certificate Processing Method
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for details on this data structure.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> VSize - This size value will always include the 4 bytes of the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> VCRC32 data and will be greater than 4 bytes.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> VData - Random data for password validation. This data is VSize
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in length and VSize must be a multiple of the encryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> block size. VCRC32 is a checksum value of VData. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> VData and VCRC32 are stored encrypted and start the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> stream of encrypted data for a file.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. Single Password Central Directory Encryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Central Directory Encryption is achieved within the .ZIP format by 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encrypting the Central Directory structure. This encapsulates the metadata 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">most often used for processing .ZIP files. Additional metadata is stored for 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redundancy in the Local Header for each file. The process of concealing 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">metadata by encrypting the Central Directory does not protect the data within 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the Local Header. To avoid information leakage from the exposed metadata 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">in the Local Header, the fields containing information about a file are masked. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Local Header:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Masking replaces the true content of the fields for a file in the Local 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Header with false information. When masked, the Local Header is not 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">suitable for streaming access and the options for data recovery of damaged
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">archives is reduced. Extra Data fields that may contain confidential
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data should not be stored within the Local Header. The value set into
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the Version needed to extract field should be the correct value needed to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">extract the file without regard to Central Directory Encryption. The fields 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">within the Local Header targeted for masking when the Central Directory is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encrypted are:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Field Name Mask Value
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ------------------ ---------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compression method 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> last mod file time 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> last mod file date 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> crc-32 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compressed size 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> uncompressed size 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file name (variable size) Base 16 value from the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> range 1 - FFFFFFFFFFFFFFFF
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> represented as a string whose
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> size will be set into the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file name length field
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Base 16 value assigned as a masked file name is simply a sequentially
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">incremented value for each file starting with 1 for the first file. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Modifications to a ZIP file may cause different values to be stored for 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">each file. For compatibility, the file name field in the Local Header 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">should never be left blank. As of Version 6.2 of this specification, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the Compression Method and Compressed Size fields are not yet masked.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Encrypting the Central Directory:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Encryption of the Central Directory does not include encryption of the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Central Directory Signature data, the ZIP64 End of Central Directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">record, the ZIP64 End of Central Directory Locator, or the End
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">of Central Directory record. The ZIP file comment data is never
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encrypted.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Before encrypting the Central Directory, it may optionally be compressed.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Compression is not required, but for storage efficiency it is assumed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">this structure will be compressed before encrypting. Similarly, this 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">specification supports compressing the Central Directory without
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">requiring that it also be encrypted. Early implementations of this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">feature will assume the encryption method applied to files matches the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encryption applied to the Central Directory.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Encryption of the Central Directory is done in a manner similar to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">that of file encryption. The encrypted data is preceded by a 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">decryption header. The decryption header is known as the Archive
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Decryption Header. The fields of this record are identical to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the decryption header preceding each encrypted file. The location
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">of the Archive Decryption Header is determined by the value in the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Start of the Central Directory field in the ZIP64 End of Central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Directory record. When the Central Directory is encrypted, the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ZIP64 End of Central Directory record will always be present.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The layout of the ZIP64 End of Central Directory record for all
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">versions starting with 6.2 of this specification will follow the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Version 2 format. The Version 2 format is as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The first 48 bytes will remain identical to that of Version 1.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The record signature for both Version 1 and Version 2 will be
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0x06064b50. Immediately following the 48th byte, which identifies 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the end of the field known as the Offset of Start of Central 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Directory With Respect to the Starting Disk Number will begin the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">new fields defining Version 2 of this record. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">New fields for Version 2:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Note: all fields stored in Intel low-byte/high-byte order.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Compression Method 2 bytes Method used to compress the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Central Directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Compressed Size 8 bytes Size of the compressed data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Original Size 8 bytes Original uncompressed size
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AlgId 2 bytes Encryption algorithm ID
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> BitLen 2 bytes Encryption key length
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Flags 2 bytes Encryption flags
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> HashID 2 bytes Hash algorithm identifier
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Hash Length 2 bytes Length of hash data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Hash Data (variable) Hash data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Compression Method accepts the same range of values as the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">corresponding field in the Central Header.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Compressed Size and Original Size values will not include the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data of the Central Directory Signature which is compressed or
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encrypted.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The AlgId, BitLen, and Flags fields accept the same range of values
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the corresponding fields within the 0x0017 record. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Hash ID identifies the algorithm used to hash the Central Directory 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data. This data does not have to be hashed, in which case the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">values for both the HashID and Hash Length will be 0. Possible 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">values for HashID are:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Algorithm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ------ ---------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0000 none
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0001 CRC32
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x8003 MD5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x8004 SHA1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">When the Central Directory data is signed, the same hash algorithm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">used to hash the Central Directory for signing should be used.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">This is recommended for processing efficiency, however, it is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">permissible for any of the above algorithms to be used independent 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">of the signing process.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Hash Data will contain the hash data for the Central Directory.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The length of this data will vary depending on the algorithm used.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Version Needed to Extract should be set to 62.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The value for the Total Number of Entries on the Current Disk will
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">be 0. These records will no longer support random access when
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encrypting the Central Directory.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">When the Central Directory is compressed and/or encrypted, the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">End of Central Directory record will store the value 0xFFFFFFFF
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">as the value for the Total Number of Entries in the Central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Directory. The value stored in the Total Number of Entries in
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the Central Directory on this Disk field will be 0. The actual
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">values will be stored in the equivalent fields of the ZIP64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">End of Central Directory record.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Decrypting and decompressing the Central Directory is accomplished
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">in the same manner as decrypting and decompressing a file.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5. Useful Tips
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Strong Encryption is always applied to a file after compression. The
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">block oriented algorithms all operate in Cypher Block Chaining (CBC) 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mode. The block size used for AES encryption is 16. All other block
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">algorithms use a block size of 8. Two ID&amp;#39;s are defined for RC2 to 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">account for a discrepancy found in the implementation of the RC2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">algorithm in the cryptographic library on Windows XP SP1 and all 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">earlier versions of Windows.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">A pseudo-code representation of the encryption process is as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Password = GetUserPassword()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">RD = Random()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ERD = Encrypt(RD,DeriveKey(SHA1(Password)))
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">For Each File
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> IV = Random()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> VData = Random()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> FileSessionKey = DeriveKey(SHA1(IV + RD))
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Encrypt(VData + VCRC32 + FileData,FileSessionKey)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Done
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The function names and parameter requirements will depend on
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the choice of the cryptographic toolkit selected. Almost any
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">toolkit supporting the reference implementations for each
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">algorithm can be used. The RSA BSAFE(r), OpenSSL, and Microsoft
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">CryptoAPI libraries are all known to work well. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Certificate Processing Method:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-----------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Certificate Processing Method for ZIP file encryption remains 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">under development. The information provided here serves as a guide
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to those interested in certificate-based data decryption. This
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">information may be subject to change in future versions of this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">specification and is subject to change without notice.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">OAEP Processing with Certificate-based Encryption:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Versions of PKZIP available during this development phase of the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">certificate processing method may set a value of 61 into the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">version needed to extract field for a file. This indicates that 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">non-OAEP key wrapping is used. This affects certificate encryption 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">only, and password encryption functions should not be affected by 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">this value. This means values of 61 may be found on files encrypted
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">with certificates only, or on files encrypted with both password
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encryption and certificate encryption. Files encrypted with both
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">methods can safely be decrypted using the password methods documented.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">OAEP stands for Optimal Asymmetric Encryption Padding. It is a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">strengthening technique used for small encoded items such as decryption
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">keys. This is commonly applied in cryptographic key-wrapping techniques
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">and is supported by PKCS #1. Versions 5.0 and 6.0 of this specification 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">were designed to support OAEP key-wrapping for certificate-based 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">decryption keys for additional security. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Support for private keys stored on Smart Cards or Tokens introduced
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a conflict with this OAEP logic. Most card and token products do 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">not support the additional strengthening applied to OAEP key-wrapped 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data. In order to resolve this conflict, versions 6.1 and above of this 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">specification will no longer support OAEP when encrypting using 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">digital certificates. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Certificate Processing Data Fields:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Certificate Processing Method of this specification defines the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">following additional data fields:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. Certificate Flag Values
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Additional processing flags that can be present in the Flags field of both 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the 0x0017 field of the central directory Extra Field and the Decryption 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">header record preceding compressed file data are:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0007 - reserved for future use
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x000F - reserved for future use
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x0100 - Indicates non-OAEP key wrapping was used. If this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this field is set, the version needed to extract must
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> be at least 61. This means OAEP key wrapping is not
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> used when generating a Master Session Key using
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ErdData.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x4000 - ErdData must be decrypted using 3DES-168, otherwise use the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> same algorithm used for encrypting the file contents.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x8000 - reserved for future use
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. CertData - Extra Field 0x0017 record certificate data structure
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The data structure used to store certificate data within the section
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">of the Extra Field defined by the CertData field of the 0x0017
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">record are as shown:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> RCount 4 bytes Number of recipients. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> HashAlg 2 bytes Hash algorithm identifier
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> HSize 2 bytes Hash size
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> SRList (var) Simple list of recipients hashed public keys
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> RCount This defines the number intended recipients whose 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> public keys were used for encryption. This identifies
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the number of elements in the SRList.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> HashAlg This defines the hash algorithm used to calculate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the public key hash of each public key used
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for encryption. This field currently supports
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> only the following value for SHA-1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x8004 - SHA1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> HSize This defines the size of a hashed public key.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> SRList This is a variable length list of the hashed 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> public keys for each intended recipient. Each 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> element in this list is HSize. The total size of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> SRList is determined using RCount * HSize.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. Reserved1 - Certificate Decryption Header Reserved1 Data:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> RCount 4 bytes Number of recipients. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> RCount This defines the number intended recipients whose 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> public keys were used for encryption. This defines
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the number of elements in the REList field defined below.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. Reserved2 - Certificate Decryption Header Reserved2 Data Structures:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> HashAlg 2 bytes Hash algorithm identifier
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> HSize 2 bytes Hash size
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> REList (var) List of recipient data elements
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> HashAlg This defines the hash algorithm used to calculate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the public key hash of each public key used
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for encryption. This field currently supports
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> only the following value for SHA-1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0x8004 - SHA1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> HSize This defines the size of a hashed public key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> defined in REHData.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> REList This is a variable length of list of recipient data. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Each element in this list consists of a Recipient
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Element data structure as follows:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Recipient Element (REList) Data Structure:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Value Size Description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ----- ---- -----------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> RESize 2 bytes Size of REHData + REKData
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> REHData HSize Hash of recipients public key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> REKData (var) Simple key blob
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> RESize This defines the size of an individual REList 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> element. This value is the combined size of the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> REHData field + REKData field. REHData is defined by
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> HSize. REKData is variable and can be calculated
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for each REList element using RESize and HSize.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> REHData Hashed public key for this recipient.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> REKData Simple Key Blob. The format of this data structure
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is identical to that defined in the Microsoft
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CryptoAPI and generated using the CryptExportKey()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> function. The version of the Simple Key Blob
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> supported at this time is 0x02 as defined by
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Microsoft.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5. Certificate Processing - Central Directory Encryption:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Central Directory Encryption using Digital Certificates will 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">operate in a manner similar to that of Single Password Central
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Directory Encryption. This record will only be present when there 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">is data to place into it. Currently, data is placed into this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">record when digital certificates are used for either encrypting 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">or signing the files within a ZIP file. When only password 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">encryption is used with no certificate encryption or digital 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">signing, this record is not currently needed. When present, this 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">record will appear before the start of the actual Central Directory 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data structure and will be located immediately after the Archive 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Decryption Header if the Central Directory is encrypted.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Archive Extra Data record will be used to store the following
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">information. Additional data may be added in future versions.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Extra Data Fields:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0x0014 - PKCS#7 Store for X.509 Certificates
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0x0016 - X.509 Certificate ID and Signature for central directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0x0019 - PKCS#7 Encryption Recipient Certificate List
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The 0x0014 and 0x0016 Extra Data records that otherwise would be 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">located in the first record of the Central Directory for digital 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">certificate processing. When encrypting or compressing the Central 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Directory, the 0x0014 and 0x0016 records must be located in the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Archive Extra Data record and they should not remain in the first 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Central Directory record. The Archive Extra Data record will also 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">be used to store the 0x0019 data. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">When present, the size of the Archive Extra Data record will be
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">included in the size of the Central Directory. The data of the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Archive Extra Data record will also be compressed and encrypted
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">along with the Central Directory data structure.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6. Certificate Processing Differences:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The Certificate Processing Method of encryption differs from the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Single Password Symmetric Encryption Method as follows. Instead
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">of using a user-defined password to generate a master session key,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cryptographically random data is used. The key material is then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">wrapped using standard key-wrapping techniques. This key material
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">is wrapped using the public key of each recipient that will need
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to decrypt the file using their corresponding private key.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">This specification currently assumes digital certificates will follow
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the X.509 V3 format for 1024 bit and higher RSA format digital
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">certificates. Implementation of this Certificate Processing Method
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">requires supporting logic for key access and management. This logic
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">is outside the scope of this specification.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">License Agreement:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-----------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The features set forth in this Section XIV (the &amp;#34;Strong Encryption 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Specification&amp;#34;) are covered by a pending patent application. Portions of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">this Strong Encryption technology are available for use at no charge 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">under the following terms and conditions. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. License Grant. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> a. NOTICE TO USER. PLEASE READ THIS ENTIRE SECTION XIV OF THE 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> APPNOTE (THE &amp;#34;AGREEMENT&amp;#34;) CAREFULLY. BY USING ALL OR ANY PORTION OF THE 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> LICENSED TECHNOLOGY, YOU ACCEPT ALL THE TERMS AND CONDITIONS OF THIS 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AGREEMENT AND YOU AGREE THAT THIS AGREEMENT IS ENFORCEABLE LIKE ANY 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> WRITTEN NEGOTIATED AGREEMENT SIGNED BY YOU. IF YOU DO NOT AGREE, DO NOT 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> USE THE LICENSED TECHNOLOGY.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> b. Definitions.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> i. &amp;#34;Licensed Technology&amp;#34; shall mean that proprietary technology now or 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> hereafter owned or controlled by PKWare, Inc. (&amp;#34;PKWARE&amp;#34;) or any 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> subsidiary or affiliate that covers or is necessary to be used to give 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> software the ability to a) extract and decrypt data from zip files 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> encrypted using any methods of data encryption and key processing which 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> are published in this APPNOTE or any prior APPNOTE, as supplemented by 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> any Additional Compatibility Information; and b) encrypt file contents 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> as part of .ZIP file processing using only the Single Password Symmetric 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Encryption Method as published in this APPNOTE or any prior APPNOTE, as 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> supplemented by any Additional Compatibility Information. For purposes 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of this AGREEMENT, &amp;#34;Additional Compatibility Information&amp;#34; means, with 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> regard to any method of data encryption and key processing published in 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this or any prior APPNOTE, any corrections, additions, or clarifications 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to the information in such APPNOTE that are required in order to give 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> software the ability to successfully extract and decrypt zip files (or, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> but solely in the case of the Single Password Symmetric Encryption Method, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to successfully encrypt zip files) in a manner interoperable with the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> actual implementation of such method in any PKWARE product that is 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> documented or publicly described by PKWARE as being able to create, or 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to extract and decrypt, zip files using that method.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ii. &amp;#34;Licensed Products&amp;#34; shall mean any products you produce that 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> incorporate the Licensed Technology.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> c. License to Licensed Technology.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> PKWARE hereby grants to you a non-exclusive license to use the Licensed 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Technology for the purpose of manufacturing, offering, selling and using 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Licensed Products, which license shall extend to permit the practice of all 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> claims in any patent or patent application (collectively, &amp;#34;Patents&amp;#34;) now or 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> hereafter owned or controlled by PKWARE in any jurisdiction in the world 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> that are infringed by implementation of the Licensed Technology. You have 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the right to sublicense rights you receive under the terms of this AGREEMENT 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for the purpose of allowing sublicensee to manufacture, offer, sell and use 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> products that incorporate all or a portion of any of your Licensed Products, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> but if you do, you agree to i) impose the same restrictions on any such 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sublicensee as these terms impose on you and ii) notify the sublicensee, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> by means chosen by you in your unfettered discretion, including a notice on 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> your web site, of the terms of this AGREEMENT and make available to each 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sublicensee the full text of this APPNOTE. Further, PKWARE hereby grants to 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> you a non-exclusive right to reproduce and distribute, in any form, copies of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this APPNOTE, without modification. Notwithstanding anything to the contrary 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in this AGREEMENT, you have the right to sublicense the rights, without any of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the restrictions described above or elsewhere in this AGREEMENT, to use, offer 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to sell and sell Licensed Technology as incorporated in executable object code 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> or byte code forms of your Licensed Products. Any sublicense to use the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Licensed Technology incorporated in a Licensed Product granted by you shall 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> survive the termination of this AGREEMENT for any reason. PKWARE warrants that 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this license shall continue to encumber the Licensed Technology regardless of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> changes in ownership of the Licensed Technology.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> d. Proprietary Notices.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> i. With respect to any Licensed Product that is distributed by you either 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> in source code form or in the form of an object code library of externally 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> callable functions that has been designed by you for incorporation into third 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> party products, you agree to include, in the source code, or in the case of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> an object code library, in accompanying documentation, a notice using the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> words &amp;#34;patent pending&amp;#34; until a patent is issued to PKWARE covering any 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> portion of the Licensed Technology or PKWARE provides notice, by means 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> chosen by PKWARE in its unfettered discretion, that it no longer has any 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> patent pending covering any portion of the Licensed Technology. With respect 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to any Licensed Product, upon your becoming aware that at least one patent has 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> been granted covering the Licensed Technology, you agree to include in any 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> revisions made by you to the documentation (or any source code distributed 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> by you) the words &amp;#34;Pat. No.&amp;#34;, or &amp;#34;Patent Number&amp;#34; and the patent number or 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> numbers of the applicable patent or patents. PKWARE shall, from time to time, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> inform you of the patent number or numbers of the patents covering the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Licensed Technology, by means chosen by PKWARE in its unfettered discretion, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> including a notice on its web site. It shall be a violation of the terms of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this AGREEMENT for you to sell Licensed Products without complying with the 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> foregoing marking provisions.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ii. You acknowledge that the terms of this AGREEMENT do not grant you any 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> license or other right to use any PKWARE trademark in connection with the sale, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> offering for sale, distribution and delivery of the Licensed Products, or in 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> connection with the advertising, promotion and offering of the Licensed Products. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> You acknowledge PKWARE&amp;#39;s ownership of the PKZIP trademark and all other marks 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> owned by PKWARE.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> e. Covenant of Compliance and Remedies.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> To the extent that you have elected to implement portions of the Licensed 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Technology, you agree to use reasonable diligence to comply with those portions 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of this Section XIV, as modified or supplemented by Additional Compatibility 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Information available to you, describing the portions of the Licensed Technology 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> that you have elected to implement. Upon reasonable request by PKWARE, you will 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> provide written notice to PKWARE identifying which version of this APPNOTE you 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> have relied upon for your implementation of any specified Licensed Product.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> If any substantial non-compliance with the terms of this AGREEMENT is determined 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to exist, you will make such changes as necessary to bring your Licensed Products 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> into substantial compliance with the terms of this AGREEMENT. If, within sixty 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> days of receipt of notice that a Licensed Product fails to comply with the terms 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> of this AGREEMENT, you fail to make such changes as necessary to bring your 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Licensed Products into compliance with the terms of this AGREEMENT, PKWARE may 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> terminate your rights under this AGREEMENT. PKWARE does not waive and expressly 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> reserves the right to pursue any and all additional remedies that are or may 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> become available to PKWARE.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> f. Warranty and Indemnification Regarding Exportation.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> You realize and acknowledge that, as between yourself and PKWARE, you are fully 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> responsible for compliance with the import and export laws and regulations of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> any country in or to which you import or export any Licensed Products, and you 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> agree to hold PKWARE harmless from any claim of violation of any such import 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> or export laws.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> g. Patent Infringement.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> You agree that you will not bring or threaten to bring any action against PKWARE 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> for infringement of the claims of any patent owned or controlled by you solely 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> as a result of PKWARE&amp;#39;s own implementation of the Licensed Technology. As its 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> exclusive remedy for your breach of the foregoing agreement, PKWARE reserves 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the right to suspend or terminate all rights granted under the terms of this 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AGREEMENT if you bring or threaten to bring any such action against PKWARE, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> effective immediately upon delivery of written notice of suspension or 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> termination to you. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> h. Governing Law. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The license granted in this AGREEMENT shall be governed by and construed under 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the laws of the State of Wisconsin and the United States. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> i. Revisions and Notice.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The license granted in this APPNOTE is irrevocable, except as expressly set 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> forth above. You agree and understand that any changes which PKWARE determines 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> to make to this APPNOTE shall be posted at the same location as the current 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> APPNOTE or at a location which will be identified by means chosen by PKWARE, 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> including a notice on its web site, and shall be available for adoption by you 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> immediately upon such posting, or at such other time as PKWARE shall determine. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Any changes to the terms of the license published in a subsequent version of 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this AGREEMENT shall be binding upon you only with respect to your products 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> that (i) incorporate any Licensed Technology (as defined in the subsequent 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AGREEMENT) that is not otherwise included in the definition of Licensed 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Technology under this AGREEMENT, or (ii) that you expressly identify are to 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> be licensed under the subsequent AGREEMENT, which identification shall be by 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> written notice with reference to the APPNOTE (version and release date or other 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> unique identifier) in which the subsequent AGREEMENT is published. PKWARE 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> agrees to identify each change to this APPNOTE by using a unique version and 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> release date identifier or other unique identifier.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> j. Warranty by PKWARE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> PKWare, Inc. warrants that it has the right to grant the license hereunder.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">XV. Change Process
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">In order for the .ZIP file format to remain a viable definition, this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">specification should be considered as open for periodic review and
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">revision. Although this format was originally designed with a 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">certain level of extensibility, not all changes in technology
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(present or future) were or will be necessarily considered in its
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">design. If your application requires new definitions to the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">extensible sections in this format, or if you would like to 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">submit new data structures, please forward your request to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zipformat@pkware.com. All submissions will be reviewed by the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ZIP File Specification Committee for possible inclusion into
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">future versions of this specification. Periodic revisions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">to this specification will be published to ensure interoperability. 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">We encourage comments and feedback that may help improve clarity 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">or content.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">XVI. Acknowledgements
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">In addition to the above mentioned contributors to PKZIP and PKUNZIP,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">I would like to extend special thanks to Robert Mahoney for suggesting
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">the extension .ZIP for this software.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">XVII. References
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">----------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Fiala, Edward R., and Greene, Daniel H., &amp;#34;Data compression with
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> finite windows&amp;#34;, Communications of the ACM, Volume 32, Number 4,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> April 1989, pages 490-505.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Held, Gilbert, &amp;#34;Data Compression, Techniques and Applications,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Hardware and Software Considerations&amp;#34;, John Wiley &amp;amp; Sons, 1987.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Huffman, D.A., &amp;#34;A method for the construction of minimum-redundancy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> codes&amp;#34;, Proceedings of the IRE, Volume 40, Number 9, September 1952,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> pages 1098-1101.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Nelson, Mark, &amp;#34;LZW Data Compression&amp;#34;, Dr. Dobbs Journal, Volume 14,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Number 10, October 1989, pages 29-37.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Nelson, Mark, &amp;#34;The Data Compression Book&amp;#34;, M&amp;amp;T Books, 1991.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Storer, James A., &amp;#34;Data Compression, Methods and Theory&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Computer Science Press, 1988
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Welch, Terry, &amp;#34;A Technique for High-Performance Data Compression&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> IEEE Computer, Volume 17, Number 6, June 1984, pages 8-19.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Ziv, J. and Lempel, A., &amp;#34;A universal algorithm for sequential data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> compression&amp;#34;, Communications of the ACM, Volume 30, Number 6,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> June 1987, pages 520-540.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Ziv, J. and Lempel, A., &amp;#34;Compression of individual sequences via
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> variable-rate coding&amp;#34;, IEEE Transactions on Information Theory,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Volume 24, Number 5, September 1978, pages 530-536.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>浅谈Service</title><link>https://opoojkk.com/posts/2024/01/14/%E6%B5%85%E8%B0%88service/</link><pubDate>Sun, 14 Jan 2024 10:26:35 +0800</pubDate><guid>https://opoojkk.com/posts/2024/01/14/%E6%B5%85%E8%B0%88service/</guid><description>&lt;p>一开始学习Android都会被告诉Android的四大组件有Activity、Service、Content Provider、Broadcast Receiver。除Service外的三者很早就接触了，并且认为有一点浅薄的理解。但是Service不是。
工作以来没有接触过与Service相关的业务，尝试过理解Service，但一直不是很理解。最近刚好用到，浅浅的记录一下最近的理解。&lt;/p>
&lt;h2 class="heading" id="作用">
 作用&lt;span class="heading__anchor"> &lt;a href="#%e4%bd%9c%e7%94%a8">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>作为四大组件之一，每个的作用都是不可替代的。Activity负责生命周期，即一个页面从创建到销毁，当前执行到了哪个阶段。Content Provider提供给外部APP一个接口，能够让外部访问内部的数据。Broadcast Receiver提供更大范围的事件Callback，可以接受其他APP的事件回调，甚至是系统状态变化，比如最常用的网络状态、电量。
我一直有一个疑问：“Service做了什么呢？”为什么可以作为最重要的四大组件之一呢？
直到最近需要写下载的代码。\&lt;/p>
&lt;blockquote>
&lt;p>&lt;code>[Service](https://developer.android.com/reference/android/app/Service?hl=zh-cn)&lt;/code> 是一种可在后台执行长时间运行的操作的&lt;a href="https://developer.android.com/guide/components/fundamentals?hl=zh-cn#Components">应用组件&lt;/a>。不提供界面。启动后，即使用户切换到其他应用，服务也可能会继续运行一段时间。此外，组件可以绑定到服务以与其交互，甚至可以执行进程间通信 (IPC)。例如，服务可以在后台处理网络事务、播放音乐、执行文件 I/O 或与 content provider 交互。&lt;/p>
&lt;/blockquote>
&lt;p>上面的文字是Android Developer官网对Service的描述。Service最重要的作用是——保活。&lt;/p>
&lt;p>假设有需要下载的场景，写一个类，类里边调用下载的代码，一样是可以的，但是应用退到后台，很快会被系统杀死，国内魔改AOSP有更严格的策略。执行下载代码时当然不希望APP在后台时就下载了，这时候Service来搞定这件事是合适的。&lt;/p>
&lt;h3 class="heading" id="后台服务">
 后台服务&lt;span class="heading__anchor"> &lt;a href="#%e5%90%8e%e5%8f%b0%e6%9c%8d%e5%8a%a1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>我们的需求只是完成下载就可以，不需要有任何提示，这时候只用一个后台服务，下载完成后执行某些逻辑。&lt;/p>
&lt;h3 class="heading" id="前台服务">
 前台服务&lt;span class="heading__anchor"> &lt;a href="#%e5%89%8d%e5%8f%b0%e6%9c%8d%e5%8a%a1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>需求更复杂一点 ，下载时需要有一个进度条，随着下载的进度不断更新，这时候需要用到前台服务。前台服务要求必须显示Notification。&lt;/p>
&lt;p>但是&lt;/p>
&lt;ol>
&lt;li>Android 8开始不允许后台应用运行后台服务；&lt;/li>
&lt;li>创建服务5s内需要显示一条通知，否则停止服务+Crash&lt;/li>
&lt;li>Android 12开始不允许后台应用创建前台服务；&lt;/li>
&lt;/ol></description></item><item><title>2023：回顾与展望</title><link>https://opoojkk.com/posts/2023/12/29/2023%E5%9B%9E%E9%A1%BE%E4%B8%8E%E5%B1%95%E6%9C%9B/</link><pubDate>Fri, 29 Dec 2023 20:59:42 +0800</pubDate><guid>https://opoojkk.com/posts/2023/12/29/2023%E5%9B%9E%E9%A1%BE%E4%B8%8E%E5%B1%95%E6%9C%9B/</guid><description>&lt;p>稀里糊涂又是一年，趁今天是2023的最后一个工作日，简简单单总结一下这一年发生的事和变化。&lt;/p>
&lt;h2 class="heading" id="工作">
 工作&lt;span class="heading__anchor"> &lt;a href="#%e5%b7%a5%e4%bd%9c">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>从年初开始打算离职，听说行情不好一直没能实践，知道六月份开始写鸿蒙应用，加上情感上的因素，综合在一起终于决定离职，还是推脱了很久才提出。&lt;/p>
&lt;p>8月底提出离职，9月底离职，gap一个月，10月底开始准备找工作，11月找到工作。工作氛围、技术氛围、周围同事的能力和发展的空间还是比较满意的，略有的遗憾的薪资起预期的少了点。&lt;/p>
&lt;p>工作中更注重方法，沟通、保证进度之类，较之前有很不一样的感触。&lt;/p>
&lt;h2 class="heading" id="生活">
 生活&lt;span class="heading__anchor"> &lt;a href="#%e7%94%9f%e6%b4%bb">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>相比工作，生活上的要更多一点。&lt;/p>
&lt;p>离职的第二天就去了厦门，从厦门去了香港，去了深圳，吃了椰子鸡。之后又去了一趟苏州，厦门和苏州这俩热门的旅游城市太网红，没有感受到本地特有的文化和烟火气，有些失望。深圳其实只待了一天，时间太短，不能做出评价。最喜欢的就是香港，感受是最真实的，和内地完全不一样的体验，尤其是地铁不用安检。后来回想其实在办理BOC银行卡的时候柜员对待香港人和内地人的态度是不一样的，希望是我敏感了。&lt;/p>
&lt;p>离职期间爬了泰山，骑了戒台寺，滑了雪，很多快乐都是第一次体会到，很开心。&lt;/p>
&lt;p>最最开心的还是结交了更多几个朋友，今年才能体会到朋友额有多珍贵，带给我很多感动，尤其是滑雪的第一天晚上给我买蛋糕过生日。&lt;/p>
&lt;h2 class="heading" id="读书">
 读书&lt;span class="heading__anchor"> &lt;a href="#%e8%af%bb%e4%b9%a6">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>原以为是要和22年读书的时间相差无几，还是能直观的看出来差很多时间的，分析原因主要是因为纠结离职和找到工作这段时间的心情没能平静下来，太过浮躁，读不下去书。&lt;/p>
&lt;p>即使时间不长，总体还是有很多收获的。印象最深刻的是下面的几本：&lt;/p>
&lt;ol>
&lt;li>《好好告别》 - 了解死亡，接受死亡。&lt;/li>
&lt;li>《我与地坛》 - 被史铁生刚毅、真诚、温柔的笔触感动。&lt;/li>
&lt;li>《台北人》 - 身居台北的各式各样的乡愁。&lt;/li>
&lt;li>《置身事内》 - 中国政府和经济的模式。&lt;/li>
&lt;li>《局外人》 - 不被社会接受的观点。&lt;/li>
&lt;li>《被嫌弃的松子的一声》 - 追求爱。&lt;/li>
&lt;li>《法治的细节》 - 法律与道德与人（在读）。&lt;/li>
&lt;/ol>
&lt;p>我一直坚信，读书的意义不一定在读了多少书，而是通过这些书获得了多少。&lt;/p>
&lt;h2 class="heading" id="最后">
 最后&lt;span class="heading__anchor"> &lt;a href="#%e6%9c%80%e5%90%8e">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>综合来看还是收货颇多，也稍稍立下几个小目标来年完成。&lt;/p>
&lt;ol>
&lt;li>读书时间150小时，日均阅读时长20+分钟。&lt;/li>
&lt;li>完成TOTP和订阅管理工具，长期维护一个。&lt;/li>
&lt;li>骑车，一天超过100公里。&lt;/li>
&lt;li>攒钱。&lt;/li>
&lt;li>交三个新朋友。&lt;/li>
&lt;li>去三个不同的城市，至少一次完全自己去。&lt;/li>
&lt;li>听一场演唱会。&lt;/li>
&lt;li>掌握一门新的技能。&lt;/li>
&lt;li>女朋友（optional）&lt;/li>
&lt;/ol></description></item><item><title>关于OTP、HOTP和TOTP</title><link>https://opoojkk.com/posts/2023/11/12/%E5%85%B3%E4%BA%8Eotphotp%E5%92%8Ctotp/</link><pubDate>Sun, 12 Nov 2023 08:43:32 +0800</pubDate><guid>https://opoojkk.com/posts/2023/11/12/%E5%85%B3%E4%BA%8Eotphotp%E5%92%8Ctotp/</guid><description>&lt;h1 class="heading" id="otp">
 OTP&lt;span class="heading__anchor"> &lt;a href="#otp">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>OTP(one-time passwords)，是MFA(multi-factor authentication)中常用的一种。添加除了账号密码之外额外的验证方式，例如某些APP会要求用户输入密码之后再次输入短信验证码，同时通过了才被允许登录，增加骇客访问被保护内容的难度。&lt;/p>
&lt;p>OTP的结果由两部分决定：seed和moving factor，seed是一个secret key，举个例子：添加两部验证(TOTP)时，可以扫码添加，还可以手动输入secret，输入的secret就是OTP中的seed。moving factor是让OTP只能使用一次的关键。&lt;/p>
&lt;p>OTP的结果一般是6-10位，常用的是6位。包括HOTP和TOTP两种方式，二者的主要差别在moving factor上。&lt;/p>
&lt;h1 class="heading" id="hotp">
 HOTP&lt;span class="heading__anchor"> &lt;a href="#hotp">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>HOTP中的H代表的是Hash-based Message Authentication Code (HMAC)，HOTP(HMAC-based One-time Password Algorithm)的moving factor是一个counter，客户端的计数器增加然后通过算法得到一串数字(HOTP值)，服务端收到后匹配，匹配成功HOTP值失效。&lt;/p>
&lt;p>详细流程参考&lt;a href="https://www.ietf.org/rfc/rfc4226.txt">RFC 4226标准文档&lt;/a>中Validation of HOTP Values部分。&lt;/p>
&lt;figure>
 &lt;img src="https://www.onelogin.com/images/patterns/text-image/hotp.png" alt="HOTP图示">&lt;/figure>&lt;h2 class="heading" id="算法主要步骤">
 算法主要步骤&lt;span class="heading__anchor"> &lt;a href="#%e7%ae%97%e6%b3%95%e4%b8%bb%e8%a6%81%e6%ad%a5%e9%aa%a4">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Java代码实现可以在上面的&lt;strong>RFC 4226标准文档&lt;/strong>中找到，描述是苍白的，建议直接看源码，非常好理解。&lt;/p>
&lt;ol>
&lt;li>将输入的long型moving factor与0xff与运算后得到只保留末尾8位保存到长度为8的byte数组，moving factor位运算向右移动8位；&lt;/li>
&lt;li>secret(seed)和byte数组通过hmac-hash得到byte数组的结果hash(长度160)；&lt;/li>
&lt;li>计算offset=hash最后一个元素和0xf做与运算，即取最后四位一个小于15的数；&lt;/li>
&lt;li>将byte数组转换成一个十进制数值，从offset开始往后的四位，每个数字取后8位，构成一个int数值binary(int数值二进制开始的位置表示符号，0表示非负数，因此固定是0)；&lt;/li>
&lt;li>拿结果为6位的举例子，binary对10^6取模，长度不够时前面补0。&lt;/li>
&lt;/ol>
&lt;h1 class="heading" id="totp">
 TOTP&lt;span class="heading__anchor"> &lt;a href="#totp">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>Time-based One-time Password (TOTP)是基于时间的。理解了上面的HOTP把couter换成时间戳就得到了TOTP的结果。我们知道一般结果会在30s或者60s之后刷新一次，可以通过当前的时间戳对1000取余得到秒数后再对30取模得到距离下一次刷新的时间间隔。
&lt;img src="https://www.onelogin.com/images/patterns/text-image/totp.png" alt="TOTP图示">&lt;/p>
&lt;h1 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h1>&lt;ul>
&lt;li>&lt;a href="https://www.onelogin.com/learn/otp-totp-hotp">What’s the Difference Between OTP, TOTP and HOTP?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%E5%9F%BA%E4%BA%8E%E6%95%A3%E5%88%97%E6%B6%88%E6%81%AF%E9%AA%8C%E8%AF%81%E7%A0%81%E7%9A%84%E4%B8%80%E6%AC%A1%E6%80%A7%E5%AF%86%E7%A0%81%E7%AE%97%E6%B3%95">基于散列消息验证码的一次性密码算法&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.ietf.org/rfc/rfc4226.txt">RFC 4226标准文档&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>OkHttp源码解析之旅</title><link>https://opoojkk.com/posts/2023/10/27/okhttp%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B9%8B%E6%97%85/</link><pubDate>Fri, 27 Oct 2023 22:53:37 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/27/okhttp%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B9%8B%E6%97%85/</guid><description>&lt;h3 class="heading" id="使用">
 使用&lt;span class="heading__anchor"> &lt;a href="#%e4%bd%bf%e7%94%a8">#&lt;/a>&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">okHttpClient&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OkHttpClient&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Builder&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">url&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;www.baidu.com&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">build&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">okHttpClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">newCall&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">execute&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">body&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">string&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="流程">
 流程&lt;span class="heading__anchor"> &lt;a href="#%e6%b5%81%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ol>
&lt;li>创建OkHttpClient对象&lt;/li>
&lt;li>创建Request对象&lt;/li>
&lt;li>同步/异步请求&lt;/li>
&lt;/ol>
&lt;h3 class="heading" id="创建okhttpclient对象">
 创建OkHttpClient对象&lt;span class="heading__anchor"> &lt;a href="#%e5%88%9b%e5%bb%baokhttpclient%e5%af%b9%e8%b1%a1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;h3 class="heading" id="创建request对象">
 创建Request对象&lt;span class="heading__anchor"> &lt;a href="#%e5%88%9b%e5%bb%barequest%e5%af%b9%e8%b1%a1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Request对象的创建由建造者完成，默认的方法是GET，headers为空。&lt;/p>
&lt;p>接着调用url方法，url方法作用是把url转换成http:..或https:&amp;hellip;形式的字符串，用字符串创建HttpUrl对象。HttpUrl对象是把Url里的域名、端口、位置(#ToASCII)等信息以每个变量的形式单独存储。再然后用上边的值作为属性，创建Request对象。Request对象与HttpUrl相比多了body、method、headers这些值。&lt;/p>
&lt;blockquote>
&lt;p>Request与HttpUrl的作用相似，作为请求的一部分。只不过包含了更多了请求信息。&lt;/p>
&lt;/blockquote>
&lt;h3 class="heading" id="创建realcall对象">
 创建RealCall对象&lt;span class="heading__anchor"> &lt;a href="#%e5%88%9b%e5%bb%barealcall%e5%af%b9%e8%b1%a1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>源码中对RealCall类的介绍：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">/**
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * Bridge between OkHttp&amp;#39;s application and network layers. This class exposes high-level application
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * layer primitives: connections, requests, responses, and streams.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> *
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * This class supports [asynchronous canceling][cancel]. This is intended to have the smallest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * blast radius possible. If an HTTP/2 stream is active, canceling will cancel that stream but not
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * the other streams sharing its connection. But if the TLS handshake is still in progress then
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * canceling may break the entire connection.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> */
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>RealCall才是真正用来请求的类。&lt;/p>
&lt;p>其中execute()为同步请求的方法入口，enqueue()为异步请求的方法入口。&lt;/p>
&lt;p>&lt;strong>execute()方法：&lt;/strong>&lt;/p>
&lt;p>源码：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">override&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fun&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">execute&lt;/span>&lt;span class="p">():&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Response&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">check&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">executed&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">compareAndSet&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">)){&lt;/span>&lt;span class="s">&amp;#34;Already Executed&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">timeout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">enter&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">callStart&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">dispatcher&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">executed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getResponseWithInterceptorChain&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">finally&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">dispatcher&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">finished&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>client.dispatcher.executed(this)里。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/** Used by [Call.execute] to signal it is in-flight. */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">internal&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fun&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">executed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">call&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RealCall&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">runningSyncCalls&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">call&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>再看&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/** Ready async calls in the order they&amp;#39;ll be run. */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">readyAsyncCalls&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayDeque&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">AsyncCall&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="cm">/** Running asynchronous calls. Includes canceled calls that haven&amp;#39;t finished yet. */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">runningAsyncCalls&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayDeque&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">AsyncCall&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="cm">/** Running synchronous calls. Includes canceled calls that haven&amp;#39;t finished yet. */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">runningSyncCalls&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayDeque&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">RealCall&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>上边只是把当前的Call放到Liist队列中，RealCallexecute()的方法需要返回值，真正的执行肯定是getResponseWithInterceptorChain()&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Throws&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">IOException&lt;/span>&lt;span class="p">::&lt;/span>&lt;span class="kd">class&lt;/span>&lt;span class="err">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nc">internal&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fun&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getResponseWithInterceptorChain&lt;/span>&lt;span class="p">():&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Response&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// Build a full stack of interceptors.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">mutableListOf&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Interceptor&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">interceptors&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RetryAndFollowUpInterceptor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BridgeInterceptor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">cookieJar&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CacheInterceptor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">cache&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ConnectInterceptor&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nf">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">forWebSocket&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">networkInterceptors&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CallServerInterceptor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">forWebSocket&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">chain&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RealInterceptorChain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">call&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">exchange&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">originalRequest&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">connectTimeoutMillis&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">connectTimeoutMillis&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">readTimeoutMillis&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">readTimeoutMillis&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">writeTimeoutMillis&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">writeTimeoutMillis&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">var&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">calledNoMoreExchanges&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">chain&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">proceed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">originalRequest&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">isCanceled&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">closeQuietly&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IOException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Canceled&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IOException&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">calledNoMoreExchanges&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">noMoreExchanges&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Throwable&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">finally&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">calledNoMoreExchanges&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">noMoreExchanges&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>把所有的拦截器加到一个队列，第一个拦截器是提供的自定义的拦截器，可以实现接口写一个。&lt;/p>
&lt;p>构造一个RealInterceptorChain对象，返回了proceed(..)方法的结果。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Throws&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">IOException&lt;/span>&lt;span class="p">::&lt;/span>&lt;span class="kd">class&lt;/span>&lt;span class="err">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nc">override&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fun&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">proceed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Request&lt;/span>&lt;span class="p">):&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Response&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">check&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">size&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">calls&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">exchange&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">check&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">exchange&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">finder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">sameHostAndPort&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">url&lt;/span>&lt;span class="p">)){&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="s">&amp;#34;network interceptor ${interceptors[index - 1]} must retain the same host and port&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">check&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">calls&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">){&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="s">&amp;#34;network interceptor ${interceptors[index - 1]} must call proceed() exactly once&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// Call the next interceptor in the chain.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">next&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">copy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">interceptor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Suppress&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;USELESS_ELVIS&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">interceptor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">intercept&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">next&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NullPointerException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;interceptor $interceptor returned null&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">exchange&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">check&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">size&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">next&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">calls&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">){&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="s">&amp;#34;network interceptor $interceptor must call proceed() exactly once&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">check&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">body&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">){&lt;/span>&lt;span class="s">&amp;#34;interceptor $interceptor returned a response with no body&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>请求时能不断调用下一个拦截器是因为在拦截器里不拦截时，又调用到RealInterceptorChain的proceed(..)里。而&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Call the next interceptor in the chain.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">next&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">copy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">interceptor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">interceptors&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>copy(..)里传入是的index+1，相当于每调用一次，index+1。&lt;/p>
&lt;p>最后在RealCall修改状态:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fun&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">finished&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">calls&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Deque&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">call&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">idleCallback&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">){&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">calls&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">remove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">call&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AssertionError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Call wasn&amp;#39;t in-flight!&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">idleCallback&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">idleCallback&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">val&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">isRunning&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">promoteAndExecute&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">isRunning&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">idleCallback&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">idleCallback&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 class="heading" id="异步执行与同步前部分相似">
 异步执行与同步前部分相似&lt;span class="heading__anchor"> &lt;a href="#%e5%bc%82%e6%ad%a5%e6%89%a7%e8%a1%8c%e4%b8%8e%e5%90%8c%e6%ad%a5%e5%89%8d%e9%83%a8%e5%88%86%e7%9b%b8%e4%bc%bc">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>异步的入口是enqueue()。源码：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">override&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fun&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">enqueue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">responseCallback&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Callback&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">check&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">executed&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">compareAndSet&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">)){&lt;/span>&lt;span class="s">&amp;#34;Already Executed&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">callStart&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">dispatcher&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">enqueue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">AsyncCall&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">responseCallback&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>AsyncCall是RealCall的内部类，实现了Runnable接口。持有外部类的引用，在调用&lt;/p>
&lt;h2 class="heading" id="拦截器">
 拦截器&lt;span class="heading__anchor"> &lt;a href="#%e6%8b%a6%e6%88%aa%e5%99%a8">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Interceptor&lt;/th>
 &lt;th style="text-align: left">作用&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>RetryAndFollowUpInterceptor&lt;/td>
 &lt;td style="text-align: left">重试、重定向&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>BridgeInterceptor&lt;/td>
 &lt;td style="text-align: left">添加header，包括压缩格式、cookie、Content-Type&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>CacheInterceptor&lt;/td>
 &lt;td style="text-align: left">判断是否可以用缓存过的响应&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>ConnectInterceptor&lt;/td>
 &lt;td style="text-align: left">建立连接，找不到时创建Socket、DNS、TCP握手&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>CallServerInterceptor&lt;/td>
 &lt;td style="text-align: left">用okio传输header和body&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div></description></item><item><title>判断Activity和Fragment状态的方式</title><link>https://opoojkk.com/posts/2023/10/27/%E5%88%A4%E6%96%ADactivity%E5%92%8Cfragment%E7%8A%B6%E6%80%81%E7%9A%84%E6%96%B9%E5%BC%8F/</link><pubDate>Fri, 27 Oct 2023 22:42:56 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/27/%E5%88%A4%E6%96%ADactivity%E5%92%8Cfragment%E7%8A%B6%E6%80%81%E7%9A%84%E6%96%B9%E5%BC%8F/</guid><description>&lt;p>理论上可以Activity或者Fragment的状态:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">currentState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isAtLeast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nc">Lifecycle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">State&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">STARTED&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>Android HTTPS抓包原理</title><link>https://opoojkk.com/posts/2023/10/27/packet-capture-in-andorid/</link><pubDate>Fri, 27 Oct 2023 22:38:20 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/27/packet-capture-in-andorid/</guid><description>&lt;h2 class="heading" id="原理">
 原理&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>正常情况下，客户端和服务端通讯先要建立https连接，客户端公国CA证书里的公钥和一个随机数生成对称秘钥，后续通讯通过对称秘钥加密解密。流程图：&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/VpzXfKD.jpg">&lt;/figure>&lt;p>抓包常用的有两种方式：&lt;/p>
&lt;ol>
&lt;li>Mitm（中间人攻击）&lt;/li>
&lt;li>安装CA证书&lt;/li>
&lt;/ol>
&lt;p>中间人攻击的原理并不是代理软件使用原始证书解密一方的请求。而是同时担当客户端和服务端的角色，客户端和服务端建立连接时，就已经在中间起作用，客户端与服务端的三次握手变成了客户端和代理软件、代理软件和服务端分别完成三次握手共两次。&lt;/p>
&lt;p>当客户端与服务端通讯时，实际上是将http请求发送给代理软件，代理软件解密后显示在面板上，显示可读的内容，再将解密后的内容通过与服务端握手时生成的对称秘钥加密后发送给服务端。&lt;/p>
&lt;p>接收服务端的响应也是类似的。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/2M7fAI8.jpg">&lt;/figure>&lt;p>总结一下实现 HTTPS 抓包的基本步骤：&lt;/p>
&lt;ul>
&lt;li>部署 MITM 代理服务器；&lt;/li>
&lt;li>通过代理等方式将网络流量归集到 MITM 主机；&lt;/li>
&lt;li>客户端信任 MITM CA 证书；&lt;/li>
&lt;li>MITM 分别与客户端和服务端建立独立连接；&lt;/li>
&lt;li>MITM 解密客户端和服务端的通信，并结构化地呈现出来。&lt;/li>
&lt;/ul>
&lt;aside>
💡 客户端安装抓包工具的证书是为了能信任抓包工具作为“服务端”时用来签名的证书，不是系统中信任的CA证书。
&lt;/aside>
&lt;aside>
💡 Charles中配置client Certificate是为了解决双向证书校验（服务端也会验证客户端证书），使用与客户端相同的证书加密。（这种情况不设置会请求失败）
还有另外一种情况是使用自签证书。（不固定证书拿不到公钥，公钥不在系统预先信任的CA中，请求失败）
&lt;/aside>
&lt;blockquote>
&lt;p>Android root后能抓到https包不是因为有证书，而是因为信任了伪造的证书。（没有双向证书校验、自签证书是可以看到的。）&lt;/p>
&lt;/blockquote></description></item><item><title>Android快捷方式开发</title><link>https://opoojkk.com/posts/2023/10/27/android%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F%E5%BC%80%E5%8F%91/</link><pubDate>Fri, 27 Oct 2023 22:34:43 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/27/android%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F%E5%BC%80%E5%8F%91/</guid><description>&lt;h1 class="heading" id="android-快捷方式">
 Android 快捷方式&lt;span class="heading__anchor"> &lt;a href="#android-%e5%bf%ab%e6%8d%b7%e6%96%b9%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>在Android系统中，快捷方式是一种非常方便的工具，可以帮助用户更快速地访问他们经常使用的应用程序或功能。用户可以在主屏幕或应用程序抽屉中创建快捷方式，并可自定义快捷方式的名称和图标。&lt;/p>
&lt;p>要在主屏幕上创建快捷方式，用户可以长按应用程序图标，然后将其拖到主屏幕上。用户还可以在主屏幕上空白的位置上长按，然后选择“快捷方式”菜单，以创建快捷方式。在应用程序抽屉中，用户可以长按应用程序图标，然后将其拖到主屏幕上以创建快捷方式。&lt;/p>
&lt;p>除了常规的应用程序快捷方式外，用户还可以创建联系人、网站、设置选项等其他类型的快捷方式。例如，用户可以创建一个联系人快捷方式，这样他们就可以快速拨打某个联系人的电话号码。用户还可以创建一个网站快捷方式，以便更快地访问他们最喜欢的网站。&lt;/p>
&lt;p>总之，Android快捷方式是一种非常有用的工具，可以帮助用户更快速地访问他们经常使用的应用程序、联系人、网站等。通过利用快捷方式，用户可以更快地完成任务并提高他们的生产力。&lt;/p>
&lt;blockquote>
&lt;p>以上内容通过Notion AI生成，没有什么卵用。&lt;/p>
&lt;/blockquote>
&lt;p>原生Android是抽屉的形式，打开抽屉才是所有的APP，桌面上显示的Icon都叫快捷方式，除了常规的把APP从抽屉拖到桌面这种形式，APP体统的shortcut也是可以创建一个单独的Icon，也成为快捷方式。当前中国大陆定制的不是，没有抽屉，更没有所谓的快捷方式。除&lt;/p>
&lt;p>今天主要试一下添加Shortcut，一下是具体内容：&lt;/p>
&lt;h2 class="heading" id="shortcut">
 Shortcut&lt;span class="heading__anchor"> &lt;a href="#shortcut">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>shortcut是对标iOS上的xx(忘了叫啥)。好处能让用户更快进到想看的Activity。&lt;/p>
&lt;h3 class="heading" id="注册方式">
 注册方式&lt;span class="heading__anchor"> &lt;a href="#%e6%b3%a8%e5%86%8c%e6%96%b9%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>静态注册&lt;/p>
&lt;p>在AndroidManifest中的MainActivity（真正意义上的，启动后第一个Activity）中引入写好的xml文件，文件中以shortcuts作为根节点。不可以通过&lt;code>ShortcutManager&lt;/code>操作静态方式添加的shortcut，会抛出异常。&lt;/p>
&lt;blockquote>
&lt;p>提示shortcuts找不到声明是正常现象，不影响内部元素提示😒。&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;li>
&lt;p>动态注册&lt;/p>
&lt;p>在Activity中通过&lt;code>ShortcutManager&lt;/code>类添加、移除、禁用shortcut，没有限制一定要在MainActivity。id相同时后面的shortcut会覆盖先添加的。&lt;/p>
&lt;p>动态注册的API通过建造者模式，传递数据也比方便。&lt;/p>
&lt;div class="table-wrapper">&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>&lt;/th>
 &lt;th>静态注册&lt;/th>
 &lt;th>动态注册&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>实现方式&lt;/td>
 &lt;td>在 AndroidManifest.xml 文件中引用写好的 xml 文件&lt;/td>
 &lt;td>通过 ShortcutManager.addDynamicShortcuts()、ShortcutManager.updateShortcuts()、ShortcutManager.removeDynamicShortcuts() 以及 ShortcutManager.removeAllDynamicShortcuts() 方法添加、更新、移除或禁用动态快捷方式&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>是否支持动态修改&lt;/td>
 &lt;td>不支持&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>是否支持读取快捷方式&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>是否支持监听快捷方式&lt;/td>
 &lt;td>不支持&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>是否支持多个快捷方式&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;td>支持&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>应用启动时是否可用&lt;/td>
 &lt;td>是&lt;/td>
 &lt;td>否&lt;/td>
 &lt;/tr>
 &lt;/tbody>
 &lt;/table>
&lt;/div> &lt;aside>
 💡 无论哪种方式，shortcutId和shortcutShortLabel都是必须的。且都要设置Action为`"android.intent.action.VIEW"`
 &lt;/aside>&lt;/li>
&lt;/ol></description></item><item><title>Android中常见的尺寸单位</title><link>https://opoojkk.com/posts/2023/10/27/android%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%9A%84%E5%B0%BA%E5%AF%B8%E5%8D%95%E4%BD%8D/</link><pubDate>Fri, 27 Oct 2023 22:27:51 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/27/android%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%9A%84%E5%B0%BA%E5%AF%B8%E5%8D%95%E4%BD%8D/</guid><description>&lt;h2 class="heading" id="px-dpi-dp">
 px dpi dp&lt;span class="heading__anchor"> &lt;a href="#px-dpi-dp">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="px">
 px&lt;span class="heading__anchor"> &lt;a href="#px">#&lt;/a>&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>像素是组成光栅图片&lt;a href="https://en.wikipedia.org/wiki/Raster_graphics">Raster graphics&lt;/a>）的最小单位。&lt;/p>
&lt;/blockquote>
&lt;p>pixel，像素点，影像显示的基本单位。(单位！荧幕可以解析的最小的点，ps中放大到最大后的一个小块)&lt;/p>
&lt;p>像素分为数码像素和屏幕像素&lt;/p>
&lt;p>&lt;strong>数码像素&lt;/strong>&lt;/p>
&lt;p>是一种虚拟化的数字，大小可以任意，或者说没有实际的物理尺寸大小。通常我们理解的一个像素是1：1的正方形块，这只是它最常见的一种形式，有的摄像机捕获的像素并不是1：1的正方向，比如：1：1.21，1：1.09，1：1.46等等。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/aj0KMVu.jpg">&lt;/figure>&lt;p>&lt;strong>屏幕像素&lt;/strong>
屏幕像素是指显示屏的像素，包括电视机，电脑显示屏，手机显示屏等等，这些像素不是虚拟的，是实实在在存在的，具有物理尺寸大小，通常是英寸-inch, 这些像素通常来说只有一种比列1：1的正方形，并且像素点之间是紧挨着的。&lt;/p>
&lt;h3 class="heading" id="dpi">
 dpi&lt;span class="heading__anchor"> &lt;a href="#dpi">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Dots Per Inch→每一英寸上包含的点个数。&lt;/p>
&lt;p>ppi，每英寸所拥有的像素数量。值越高，单位英尺的像素越多，画面当然越精细。&lt;/p>
&lt;p>公式&lt;/p>
&lt;p>&lt;em>dpi&lt;/em>=(对角线)(宽2+高2)(&lt;em>px&lt;/em>)/屏幕尺寸&lt;/p>
&lt;p>$$
dpi=\frac{\sqrt{(对角线)(宽2+高2)(px)}}{屏幕尺寸}
$$&lt;/p>
&lt;h3 class="heading" id="ppi">
 PPI&lt;span class="heading__anchor"> &lt;a href="#ppi">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Pixels Per Inch→每英尺像素&lt;/p>
&lt;h3 class="heading" id="dp">
 dp&lt;span class="heading__anchor"> &lt;a href="#dp">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Density-independent Pixels， 这个是Android基于物理设备的&lt;strong>ppi&lt;/strong>抽象出来的一个单位。它是以&lt;strong>160dpi&lt;/strong>的屏幕为基准定义的，在&lt;strong>160dpi&lt;/strong>的屏幕的屏幕上&lt;strong>1dp=1px&lt;/strong>。&lt;/p>
&lt;h3 class="heading" id="dip">
 &lt;strong>dip&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#dip">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>就是dp，另一种说法。&lt;/p>
&lt;h2 class="heading" id="ppi与dpi的区别">
 &lt;strong>ppi与dpi的区别&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#ppi%e4%b8%8edpi%e7%9a%84%e5%8c%ba%e5%88%ab">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="像素密度ppi">
 像素密度（PPI）&lt;span class="heading__anchor"> &lt;a href="#%e5%83%8f%e7%b4%a0%e5%af%86%e5%ba%a6ppi">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>表示每英寸长度上有多少个&lt;strong>像素&lt;/strong>，又叫像素数目。像素越多，代表画面更细腻更清晰。我们常说的视网膜屏幕（Retina），就是指PPI较普通屏幕要高。&lt;/p>
&lt;p>ppi(pixels per inch)是图像分辨率的单位，图像ppi值越高，画面的细节就越丰富，因为单位面积的像素数量更多。&lt;/p>
&lt;p>上述我们知道了屏幕尺寸是指对角线长度，如果又知道了屏幕的分辨率（即知道了宽高的像素值），那么宽高和对角线就形成了一个垂直三角形。利用勾股定理，可以算出对角线的像素值了。而又知道了对角线的英寸值，那么就可以算出屏幕的PPI值了&lt;/p>
&lt;figure>
 &lt;img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/8/27/16cd0ed0b7bee0d1~tplv-t2oaga2asx-zoom-in-crop-mark:3024:0:0:0.awebp">&lt;/figure>&lt;h3 class="heading" id="dpi-1">
 DPI&lt;span class="heading__anchor"> &lt;a href="#dpi-1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>DPI（Dots Per Inch，每英寸点数）是一个量度单位，用于点阵数码影像，指每一英寸长度中，取样、可显示或输出&lt;strong>点的数目&lt;/strong>。这里说的点，类似打印的“墨点”，打印成像是由这些墨点成线，线成面这样组合而成。&lt;/p>
&lt;p>它是一个输出分辨率（打印分辨率），常用于描述打印机的打印精度，一般来说，DPI值越高，表明打印机的打印精度越高。它表示每英寸所能打印的点数，即打印精度&lt;/p>
&lt;p>一般的激光打印机的输出分辨率是300dpi-600dpi，印刷的照排机达到1200dpi-2400dpi，常见的冲印一般在150dpi到300dpi之间。&lt;/p>
&lt;p>图像的像素、打印分辨率和打印尺寸的关系如下：&lt;/p>
&lt;blockquote>
&lt;p>图像的横向（竖向）像素数=打印横向（竖向）分辨率×打印的横向（竖向）尺寸&lt;/p>
&lt;/blockquote>
&lt;p>例如：希望打印照片的尺寸是&lt;code>4*3inch&lt;/code>，而打印分辨率横向和竖向都是&lt;code>300dpi&lt;/code>，则需要照相机采集的像素数至少为&lt;code>（300*4）*（300*3）=1080000&lt;/code>像素，约一百万像素。采集的像素数过低（采集图像机器的PPI决定）会降低图像的打印质量，过高也不能提升打印质量。&lt;/p>
&lt;p>ppi和dpi经常都会出现混用现象。但是他们所用的领域也存在区别。从技术角度说，“像素”只存在于电脑显示领域，而“点”只出现于打印或印刷领域。&lt;/p>
&lt;h3 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h3>&lt;ol>
&lt;li>&lt;a href="https://www.zhihu.com/question/21149600/answer/724435661">分辨率和像素是什么关系？&lt;/a>&lt;/li>
&lt;li>稀土掘金技术社区&lt;/li>
&lt;/ol></description></item><item><title>透明状态栏和透明导航栏</title><link>https://opoojkk.com/posts/2023/10/27/%E9%80%8F%E6%98%8E%E7%8A%B6%E6%80%81%E6%A0%8F%E5%92%8C%E9%80%8F%E6%98%8E%E5%AF%BC%E8%88%AA%E6%A0%8F/</link><pubDate>Fri, 27 Oct 2023 22:22:46 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/27/%E9%80%8F%E6%98%8E%E7%8A%B6%E6%80%81%E6%A0%8F%E5%92%8C%E9%80%8F%E6%98%8E%E5%AF%BC%E8%88%AA%E6%A0%8F/</guid><description>&lt;blockquote>
&lt;p>设置Window Flag要在setContentView前。不在前目前发现改变状态栏不会生效&lt;/p>
&lt;/blockquote>
&lt;h2 class="heading" id="实现透明状态栏">
 实现透明状态栏&lt;span class="heading__anchor"> &lt;a href="#%e5%ae%9e%e7%8e%b0%e9%80%8f%e6%98%8e%e7%8a%b6%e6%80%81%e6%a0%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>透明状态栏以来官方提供的API，重要改动主要有三个：&lt;/p>
&lt;ol>
&lt;li>Android 4.4&lt;/li>
&lt;li>Android 5.0&lt;/li>
&lt;li>Android 6.0&lt;/li>
&lt;/ol>
&lt;h3 class="heading" id="android-44">
 Android 4.4&lt;span class="heading__anchor"> &lt;a href="#android-44">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>从4.4开始支持透明状态栏，这个版本是半透明，有一层遮罩：&lt;/p>
&lt;h3 class="heading" id="android-50">
 Android 5.0&lt;span class="heading__anchor"> &lt;a href="#android-50">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>真正支持透明状态栏，可以设置状态栏的颜色。&lt;/p>
&lt;p>&lt;strong>实现方式&lt;/strong>&lt;/p>
&lt;p>给window中的DecorView设置*&lt;code>systemUiVisibility&lt;/code>&lt;em>：&lt;code>View.*SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN*&lt;/code>和&lt;code>View.*SYSTEM_UI_FLAG_LAYOUT_STABLE&lt;/code>。&lt;/em>&lt;/p>
&lt;p>同时给window添加flag：&lt;em>&lt;code>FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS&lt;/code>&lt;/em>&lt;/p>
&lt;p>&lt;strong>含义&lt;/strong>&lt;/p>
&lt;p>&lt;em>&lt;code>SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN&lt;/code>&lt;/em>：状态栏和导航栏悬浮在View上方&lt;/p>
&lt;p>&lt;em>&lt;code>SYSTEM_UI_FLAG_LAYOUT_STABLE&lt;/code>&lt;/em>：让状态栏、导航栏稳定，不随输入状态改变。&lt;/p>
&lt;p>&lt;em>&lt;code>FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS&lt;/code>&lt;/em>：绘制的内容可以到状态栏和导航栏的区域。&lt;/p>
&lt;h3 class="heading" id="android-60">
 Android 6.0&lt;span class="heading__anchor"> &lt;a href="#android-60">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>支持了导航栏上icon和文字颜色的反色&lt;/p>
&lt;p>也是通过window中的DecorView设置*&lt;code>systemUiVisibility&lt;/code>。*&lt;/p>
&lt;p>亮色状态栏：&lt;code>View.*SYSTEM_UI_FLAG_LIGHT_STATUS_BAR*&lt;/code>&lt;/p>
&lt;p>暗色状态栏：&lt;code>View.*SYSTEM_UI_FLAG_LIGHT_STATUS_BAR&lt;/code>取&lt;code>反&lt;/code>*&lt;/p>
&lt;h3 class="heading" id="android-11">
 Android 11&lt;span class="heading__anchor"> &lt;a href="#android-11">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>弃用Flag的形式。&lt;/p>
&lt;p>内容可以绘制到状态栏和导航栏的区域：&lt;/p>
&lt;p>&lt;code>WindowCompat.setDecorFitsSystemWindows(window, false)&lt;/code>&lt;/p>
&lt;p>状态栏颜色要求使用&lt;code>WindowInsetsController&lt;/code>，或是兼容的&lt;code>WindowInsetsControllerCompat&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">val&lt;/span> &lt;span class="py">windowInsetsControllerCompat&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">WindowCompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getInsetsController&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">window&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">decorView&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">windowInsetsControllerCompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">isAppearanceLightStatusBars&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>&lt;strong>往&lt;code>DecorView&lt;/code>上加了marginBottom，底部的navigationBar不见了&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;p>默认的navigationBar在DecorViewView中LinearLayout的下面，紧挨着。
statusBar添加flag后是悬浮在ContentView上方。&lt;/p>
&lt;p>猜测是因为默认的flag的关系。具体不清楚
从代码可以确定是因为设置marginBottom让NavigationBar不显示了。&lt;/p>
&lt;blockquote>
&lt;p>💡 使用官方推荐的&lt;code>setOnApplyWindowInsetsListener&lt;/code>更合适，通过dimen拿到高度不准确，且在隐藏时是0。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>setOnApplyWindowInsetsListener&lt;/code>调用时机：&lt;/p>
&lt;p>从ViewGroup分发至子View，流程类似事件分发中的View分发部分，开始的源头则是ViewRootImpl调用布局绘制&lt;code>performTraversals&lt;/code>的方法&lt;/p>
&lt;p>另一种途径是调用了&lt;code>requestFitSystemWindows&lt;/code>方法，其实还是因为需要重新布局绘制。&lt;/p></description></item><item><title>VPN代理模式：TUN与TAP对比</title><link>https://opoojkk.com/posts/2023/10/27/vpn%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8Ftun%E4%B8%8Etap%E5%AF%B9%E6%AF%94/</link><pubDate>Fri, 27 Oct 2023 22:17:51 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/27/vpn%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8Ftun%E4%B8%8Etap%E5%AF%B9%E6%AF%94/</guid><description>&lt;h2 class="heading" id="vpn">
 VPN&lt;span class="heading__anchor"> &lt;a href="#vpn">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="https代理">
 http(s)代理&lt;span class="heading__anchor"> &lt;a href="#https%e4%bb%a3%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>第一种是 &lt;a href="http://tools.ietf.org/html/rfc7230">RFC 7230 - HTTP/1.1: Message Syntax and Routing&lt;/a>（即修订后的 RFC 2616，HTTP/1.1 协议的第一部分）描述的普通代理。这种代理扮演的是「中间人」角色，对于连接到它的客户端来说，它是服务端；对于要连接的服务端来说，它是客户端。它就负责在两端之间来回传送 HTTP 报文。&lt;/p>
&lt;p>第二种是 &lt;a href="https://tools.ietf.org/html/draft-luotonen-web-proxy-tunneling-01">Tunneling TCP based protocols through Web proxy servers&lt;/a>（通过 Web 代理服务器用隧道方式传输基于 TCP 的协议）描述的隧道代理。它通过 HTTP 协议正文部分（Body）完成通讯，以 HTTP 的方式实现任意基于 TCP 的应用层协议代理。这种代理使用 HTTP 的 CONNECT 方法建立连接，但 CONNECT 最开始并不是 RFC 2616 - HTTP/1.1 的一部分，直到 2014 年发布的 HTTP/1.1 修订版中，才增加了对 CONNECT 及隧道代理的描述，&lt;a href="https://tools.ietf.org/html/rfc7231#section-4.3.6">详见 RFC 7231 - HTTP/1.1: Semantics and Content&lt;/a>。实际上这种代理早就被广泛实现。&lt;/p>
&lt;h4 class="heading" id="中间人攻击代理">
 中间人攻击代理&lt;span class="heading__anchor"> &lt;a href="#%e4%b8%ad%e9%97%b4%e4%ba%ba%e6%94%bb%e5%87%bb%e4%bb%a3%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h4>&lt;figure>
 &lt;img src="https://st.imququ.com/i/avif/static/uploads/2015/11/web_proxy.png.avif" alt="中间人攻击">&lt;/figure>&lt;p>客户端与通信的服务器之间经过另一个代理服务器，客户端请求时先将请求发送至代理服务器，代理服务器再发送至真实的服务器。同样的，服务器响应时，也先将相应发送至代理服务器，代理服务器再发送至实际的客户端。&lt;/p>
&lt;p>这种方式与
&lt;a href="https://opoojkk.com/posts/2023/10/27/packet-capture-in-andorid/" title="抓包的原理">抓包的原理&lt;/a>相同，只能代理http请求，如果代理https时客户端或服务端有证书校验（certificate pinning）， 就不能通过。原因是网络请求实际时代理服务器发送的，且只能从服务器拿到证书和公钥，拿不到私钥就没有办法生成通过证书校验。&lt;/p>
&lt;h3 class="heading" id="http隧道代理">
 http隧道代理&lt;span class="heading__anchor"> &lt;a href="#http%e9%9a%a7%e9%81%93%e4%bb%a3%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h3>&lt;figure>
 &lt;img src="https://st.imququ.com/i/avif/static/uploads/2015/11/web_tunnel.png.avif" alt="隧道代理">&lt;/figure>&lt;p>客户端和服务端通过隧道传递消息，隧道只做转发。&lt;/p>
&lt;p>http 1.1提供的功能。&lt;/p>
&lt;h2 class="heading" id="tun-模式">
 TUN 模式&lt;span class="heading__anchor"> &lt;a href="#tun-%e6%a8%a1%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="原理">
 原理&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e7%90%86">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>TUN 模式是一种网络层（第三层）代理模式。在这种模式下，Clash 创建了一个虚拟的网络接口（通常称为 TUN 接口），将流量从应用程序重定向到这个接口。然后，Clash 可以捕获和处理从该接口发出的数据包，进行代理操作，然后将它们发送到目标服务器，最后将响应数据包发送回虚拟接口，以便返回给应用程序。&lt;/p>
&lt;h3 class="heading" id="应用">
 应用&lt;span class="heading__anchor"> &lt;a href="#%e5%ba%94%e7%94%a8">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>UN 模式通常用于代理 UDP 流量和 IP 层数据，例如 VPN 连接。它更适合用于路由级别的代理，允许您在操作系统级别实现网络代理。&lt;/p>
&lt;h3 class="heading" id="优点">
 优点&lt;span class="heading__anchor"> &lt;a href="#%e4%bc%98%e7%82%b9">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>TUN 模式可以捕获整个 IP 数据包，因此对于需要代理整个网络连接的应用程序非常有用。它通常比 TAP 模式更高效，因为它仅操作网络层。&lt;/p>
&lt;h2 class="heading" id="tap-模式">
 TAP 模式&lt;span class="heading__anchor"> &lt;a href="#tap-%e6%a8%a1%e5%bc%8f">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="原理-1">
 原理&lt;span class="heading__anchor"> &lt;a href="#%e5%8e%9f%e7%90%86-1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>TAP 模式是一种数据链路层（第二层）代理模式。在这种模式下，Clash 创建虚拟以太网适配器（通常称为 TAP 适配器），并将其连接到物理网络适配器上。应用程序通过这个虚拟适配器发送和接收数据包，Clash 可以捕获和处理这些数据包，然后将它们发送到目标服务器，最后将响应数据包发送回虚拟适配器以返回给应用程序。&lt;/p>
&lt;h3 class="heading" id="应用-1">
 应用&lt;span class="heading__anchor"> &lt;a href="#%e5%ba%94%e7%94%a8-1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>TAP 模式通常用于代理需要以太网级别连接的应用程序，如一些在线游戏或其他需要 MAC 地址绑定的应用程序。&lt;/p>
&lt;h3 class="heading" id="优点-1">
 优点&lt;span class="heading__anchor"> &lt;a href="#%e4%bc%98%e7%82%b9-1">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>TAP 模式允许代理以太网级别的连接，这对某些特定的应用程序非常有用。然而，它通常比 TUN 模式更复杂，因为它需要处理数据链路层的细节。&lt;/p>
&lt;p>💡 一般情况下，电脑设置VPN是设置系统代理，由软件决定要不要通过代理。TUN模式和TAP模式都是通过TCP/IP中更底层的让计算机中所有应用全都走代理。&lt;/p>
&lt;h2 class="heading" id="taptun代理示意图">
 TAP/TUN代理示意图&lt;span class="heading__anchor"> &lt;a href="#taptun%e4%bb%a3%e7%90%86%e7%a4%ba%e6%84%8f%e5%9b%be">#&lt;/a>&lt;/span>
&lt;/h2>&lt;figure>
 &lt;img src="https://www.zhaohuabing.com/img/2020-02-24-linux-taptun/linux-tuntap.png" alt="TAP和TUN代理">&lt;/figure>&lt;h2 class="heading" id="参考">
 参考&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/CONNECT">CONNECT - HTTP | MDN&lt;/a>
&lt;a href="https://imququ.com/post/web-proxy.html">HTTP 代理原理及实现（一）&lt;/a>
&lt;a href="https://www.zhaohuabing.com/post/2020-02-24-linux-taptun/">Linux Tun/Tap 介绍&lt;/a>&lt;/p></description></item><item><title>Toast异常：BadTokenException</title><link>https://opoojkk.com/posts/2023/10/27/toast%E5%BC%82%E5%B8%B8badtokenexception/</link><pubDate>Fri, 27 Oct 2023 21:18:04 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/27/toast%E5%BC%82%E5%B8%B8badtokenexception/</guid><description>&lt;p>在Android版本7.1.2（API25）及以下会有BadTokenException。到了Android 8.0官方修复了这个问题。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// aapi 25&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">handleShow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">IBinder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">windowToken&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">localLOGV&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">v&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;HANDLE SHOW: &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; mView=&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; mNextView=&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mNextView&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mNextView&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// remove the old view if necessary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">handleHide&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mNextView&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getContext&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getApplicationContext&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">packageName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getContext&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getOpPackageName&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getContext&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mWM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">WindowManager&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getSystemService&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">WINDOW_SERVICE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// We can resolve the Gravity here by using the Locale for getting&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// the layout direction&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Configuration&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getContext&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getResources&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getConfiguration&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getAbsoluteGravity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mGravity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getLayoutDirection&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">HORIZONTAL_GRAVITY_MASK&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">FILL_HORIZONTAL&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">horizontalWeight&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">0f&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">VERTICAL_GRAVITY_MASK&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">FILL_VERTICAL&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">verticalWeight&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">0f&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">x&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mX&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">y&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mY&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">verticalMargin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mVerticalMargin&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">horizontalMargin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mHorizontalMargin&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">packageName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">packageName&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">hideTimeoutMilliseconds&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mDuration&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Toast&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LENGTH_LONG&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LONG_DURATION_TIMEOUT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SHORT_DURATION_TIMEOUT&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">token&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">windowToken&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getParent&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">localLOGV&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">v&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;REMOVE! &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; in &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mWM&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">removeView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">localLOGV&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">v&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;ADD! &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; in &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mWM&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">trySendAccessibilityEvent&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>在Android8.0中官方加上了try&amp;hellip;catch&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// api 26&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">handleShow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">IBinder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">windowToken&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">localLOGV&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">v&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;HANDLE SHOW: &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; mView=&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; mNextView=&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mNextView&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// If a cancel/hide is pending - no need to show - at this point&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// the window token is already invalid and no need to do any work.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mHandler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">hasMessages&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CANCEL&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mHandler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">hasMessages&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HIDE&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mNextView&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// remove the old view if necessary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">handleHide&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mNextView&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getContext&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getApplicationContext&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">packageName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getContext&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getOpPackageName&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getContext&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mWM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">WindowManager&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getSystemService&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">WINDOW_SERVICE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// We can resolve the Gravity here by using the Locale for getting&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// the layout direction&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Configuration&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getContext&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getResources&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getConfiguration&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getAbsoluteGravity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mGravity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getLayoutDirection&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">HORIZONTAL_GRAVITY_MASK&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">FILL_HORIZONTAL&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">horizontalWeight&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">0f&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">gravity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">VERTICAL_GRAVITY_MASK&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gravity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">FILL_VERTICAL&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">verticalWeight&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">0f&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">x&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mX&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">y&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mY&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">verticalMargin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mVerticalMargin&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">horizontalMargin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mHorizontalMargin&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">packageName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">packageName&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">hideTimeoutMilliseconds&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mDuration&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Toast&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LENGTH_LONG&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LONG_DURATION_TIMEOUT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SHORT_DURATION_TIMEOUT&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">token&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">windowToken&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getParent&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">localLOGV&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">v&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;REMOVE! &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; in &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mWM&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">removeView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">localLOGV&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">v&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;ADD! &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; in &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Since the notification manager service cancels the token right&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// after it notifies us to cancel the toast there is an inherent&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// race and we may attempt to add a window after the token has been&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// invalidated. Let us hedge against that.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mWM&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mView&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mParams&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">trySendAccessibilityEvent&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">WindowManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">BadTokenException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="cm">/* ignore */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>使用SpannableStringBuilder注意事项</title><link>https://opoojkk.com/posts/2023/10/27/%E4%BD%BF%E7%94%A8spannablestringbuilder%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/</link><pubDate>Fri, 27 Oct 2023 21:11:51 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/27/%E4%BD%BF%E7%94%A8spannablestringbuilder%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/</guid><description>&lt;p>需求中有一行文字显示的大小、颜色、下划线、加粗等不同，可以用几个TextView实现，也可以用SpannableStringBuilder在一个TextView上实现，还可以在string的文件中用H5的语法写。&lt;/p>
&lt;p>&lt;strong>后两种的差别主要在点击事件上，用H5的语法没法对一个字符串中的几个字设置点击事件。&lt;/strong>&lt;/p>
&lt;p>使用时把多个字符串用append()方法拼接上，设置需要用到的Span。&lt;/p>
&lt;ul>
&lt;li>设置Span时要注意按照顺序&lt;/li>
&lt;li>设置颜色、点击事件时Span对象不能复用，用几个就要创建几个&lt;/li>
&lt;li>对ClickableSpan默认有下划线，可以重写updateDrawState方法去掉&lt;/li>
&lt;li>用ClickableSpan时，除了用setText，还要用lawAndPrivacyTv.setMovementMethod(LinkMovementMethod.getInstance())否则点击事件不生效&lt;/li>
&lt;li>另，重写的updateDrawState方法里也可以设置默认颜色(setColor)&lt;/li>
&lt;/ul>
&lt;p>&lt;code>Spannable.SPAN_EXCLUSIVE_EXCLUSIVE&lt;/code>：前后都不包括，即在指定范围的前面和后面插入新字符都不会应用新样式
&lt;code>Spannable.SPAN_EXCLUSIVE_INCLUSIVE&lt;/code>：前面不包括，后面包括。即仅在范围字符的后面插入新字符时会应用新样式
&lt;code>Spannable.SPAN_INCLUSIVE_EXCLUSIVE&lt;/code>：前面包括，后面不包括。
&lt;code>Spannable.SPAN_INCLUSIVE_INCLUSIVE&lt;/code>：前后都包括。&lt;/p>
&lt;p>源码：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">initLawAndPrivacyTv&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">position&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">agree&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Utils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">string&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">text_agree&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">privacy&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Utils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">string&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">privacy_policy&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">and&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Utils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">string&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">text_and&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">law&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Utils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">string&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">law_policy&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ForegroundColorSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">agreeBlackSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ForegroundColorSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Color&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">BLACK&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ForegroundColorSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">andBlackSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ForegroundColorSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Color&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">BLACK&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ForegroundColorSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">privacyBlueSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ForegroundColorSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Utils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getColor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">color&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">color_3091F2&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ForegroundColorSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lawBlueSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ForegroundColorSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Utils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getColor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">color&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">color_3091F2&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ClickableSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">privacyClickableSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ClickableSpan&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">onClick&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">widget&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">jumpToPrivacyPolicy&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">updateDrawState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TextPaint&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">textPaint&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">updateDrawState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">textPaint&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">textPaint&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setUnderlineText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ClickableSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lawClickableSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ClickableSpan&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">onClick&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">widget&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">JumpToLawPolicy&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">updateDrawState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@NonNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TextPaint&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">textPaint&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">updateDrawState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">textPaint&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">textPaint&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setUnderlineText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">SpannableStringBuilder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SpannableStringBuilder&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">agree&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">agreeBlackSpan&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPAN_EXCLUSIVE_EXCLUSIVE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">position&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">agree&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">law&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lawBlueSpan&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">position&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPAN_EXCLUSIVE_EXCLUSIVE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lawClickableSpan&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">position&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPAN_EXCLUSIVE_EXCLUSIVE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">position&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">law&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">and&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">andBlackSpan&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">position&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPAN_EXCLUSIVE_EXCLUSIVE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">position&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">and&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">privacy&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">privacyBlueSpan&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">position&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPAN_EXCLUSIVE_EXCLUSIVE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">privacyClickableSpan&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">position&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spanned&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPAN_EXCLUSIVE_EXCLUSIVE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">lawAndPrivacyTv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">stringBuilder&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// 不设置点击事件不起作用&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">lawAndPrivacyTv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setMovementMethod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinkMovementMethod&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInstance&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>除了SpannableStringBuilder，还有个SpannableString，区别和String与StringBuilder的区别相同。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>补充：&lt;/strong>&lt;/p>
&lt;p>四种flag在按照顺序拼接文字时没有区别，区别在&lt;code>SpannableStringBuilder#insert&lt;/code>时，如下(x为增加的)：&lt;/p>
&lt;p>SPAN_INCLUSIVE_INCLUSIVE&lt;/p>
&lt;figure>
 &lt;img src="https://i1.wp.com/i.stack.imgur.com/Gbpw5.png">&lt;/figure>&lt;p>SPAN_INCLUSIVE_EXCLUSIVE&lt;/p>
&lt;figure>
 &lt;img src="https://i1.wp.com/i.stack.imgur.com/nzqqi.png">&lt;/figure>&lt;p>SPAN_EXCLUSIVE_INCLUSIVE&lt;/p>
&lt;figure>
 &lt;img src="https://i1.wp.com/i.stack.imgur.com/gIgaD.png">&lt;/figure>&lt;p>SPAN_EXCLUSIVE_EXCLUSIVE&lt;/p>
&lt;figure>
 &lt;img src="https://i1.wp.com/i.stack.imgur.com/ZPCyz.png">&lt;/figure>&lt;p>Java代码：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">myString&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;01234&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">start&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">end&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">3&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">spanFlag&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spannable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SPAN_INCLUSIVE_INCLUSIVE&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// this is what is changing&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// set the span&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">SpannableStringBuilder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">spannableString&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SpannableStringBuilder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">myString&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">ForegroundColorSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">foregroundSpan&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ForegroundColorSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Color&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">RED&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">spannableString&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setSpan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">foregroundSpan&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">start&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">end&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">spanFlag&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// insert the text after the span has already been set&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// (inserting at start index second so that end index doesn&amp;#39;t get messed up)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">spannableString&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">insert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">end&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s">&amp;#34;x&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">spannableString&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">insert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">start&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s">&amp;#34;x&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">textView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">spannableString&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>你需要知道的inlcude标签属性</title><link>https://opoojkk.com/posts/2023/10/27/%E4%BD%A0%E9%9C%80%E8%A6%81%E7%9F%A5%E9%81%93%E7%9A%84inlcude%E6%A0%87%E7%AD%BE%E5%B1%9E%E6%80%A7/</link><pubDate>Fri, 27 Oct 2023 20:58:39 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/27/%E4%BD%A0%E9%9C%80%E8%A6%81%E7%9F%A5%E9%81%93%E7%9A%84inlcude%E6%A0%87%E7%AD%BE%E5%B1%9E%E6%80%A7/</guid><description>&lt;p>有了解可以通过&lt;code>&amp;lt;include&amp;gt;&lt;/code>标签导入其他布局xml以实现服用的效果。但，一直不明白到底是怎么实现的。这篇就是对怎么实现的探索。&lt;/p>
&lt;p>官网说明：&lt;/p>
&lt;blockquote>
&lt;p>如果要使用 &lt;code>&amp;lt;include&amp;gt;&lt;/code> 标记来替换布局属性，您必须同时替换 &lt;code>android:layout_height&lt;/code> 和 &lt;code>android:layout_width&lt;/code> 才能让其他布局属性生效。&lt;/p>
&lt;/blockquote>
&lt;p>为什么官网会这么说明，又是怎么实现呢？&lt;/p>
&lt;h2 class="heading" id="入口">
 入口&lt;span class="heading__anchor"> &lt;a href="#%e5%85%a5%e5%8f%a3">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>不管是从Activity还是Fragment设置布局，最后都会调用到&lt;code>LayoutInflater#inflate&lt;/code>方法。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Inflate a new view hierarchy from the specified xml resource. Throws
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * {@linkInflateException} if there is an error.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @paramresourceID for an XML layout resource to load (e.g.,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *&amp;lt;code&amp;gt;R.layout.main_page&amp;lt;/code&amp;gt;)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @paramrootOptional view to be the parent of the generated hierarchy (if
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *&amp;lt;em&amp;gt;attachToRoot&amp;lt;/em&amp;gt; is true), or else simply an object that
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * provides a set of LayoutParams values for root of the returned
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * hierarchy (if&amp;lt;em&amp;gt;attachToRoot&amp;lt;/em&amp;gt; is false.)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @paramattachToRootWhether the inflated hierarchy should be attached to
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * the root parameter? If false, root is only used to create the
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * correct subclass of LayoutParams for the root view in the XML.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @return The root View of the inflated hierarchy. If root was supplied and
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * attachToRoot is true, this is root; otherwise it is the root of
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * the inflated XML file.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">inflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@LayoutRes&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nd">@Nullable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Resources&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getContext&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getResources&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DEBUG&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">d&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;INFLATING from resource: \&amp;#34;&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getResourceName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;\&amp;#34; (&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Integer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">toHexString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;)&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tryInflatePrecompiled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">XmlResourceParser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">inflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">finally&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">close&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>共有两处调用其他方法，分别是&lt;code>tryInflatePrecompiled&lt;/code>和&lt;code>inflate&lt;/code>。&lt;/p>
&lt;p>&lt;code>tryInflatePrecompiled&lt;/code>方法中变量&lt;code>mUseCompiledView&lt;/code>为false时会return null。找到调用处只有一处可能为true，根据注释说明是用于单元测试，那么实际的方法就是&lt;code>inflate&lt;/code>了。&lt;/p>
&lt;p>inflate内部先判断&lt;code>name&lt;/code>的值是否和&lt;code>TAG_NAME&lt;/code>相同：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG_MERGE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">equals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">attachToRoot&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">InflateException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;lt;merge /&amp;gt; can be used only with a valid &amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;ViewGroup root and attachToRoot=true&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">rInflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">inflaterContext&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>找到TAG_MERGE的定义处，可以看到除了&lt;code>TAG_MERGE&lt;/code>还有*&lt;code>TAG_INCLUDE&lt;/code>，&lt;em>且其中抛出的异常是说&lt;code>&amp;lt;merge /&amp;gt;&lt;/code>，大胆推测&lt;/em>&lt;code>TAG_INCLUDE&lt;/code>*就是我们要找的&lt;code>&amp;lt;include&amp;gt;&lt;/code>的解析。&lt;/p>
&lt;p>顺着可以找到用到*&lt;code>TAG_INCLUDE&lt;/code>*的位置：&lt;code>rInflate&lt;/code>方法。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG_INCLUDE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">equals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getDepth&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">InflateException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;lt;include /&amp;gt; cannot be the root element&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">parseInclude&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>抛出异常的情况也是说&lt;code>&amp;lt;include /&amp;gt;&lt;/code>标签，进一步证实，这就是我们要找的。那么&lt;code>parseInclude&lt;/code>方法肯定就是具体的解析方法了。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">precompiled&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tryInflatePrecompiled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">layout&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getResources&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cm">/*attachToRoot=*/&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">precompiled&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">XmlResourceParser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childParser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getResources&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">layout&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AttributeSet&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childAttrs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Xml&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">asAttributeSet&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">childParser&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">while&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childParser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">next&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">XmlPullParser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">START_TAG&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">XmlPullParser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">END_DOCUMENT&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Empty.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">XmlPullParser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">START_TAG&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">InflateException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">getParserStateDescription&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childAttrs&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;: No start tag found!&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childParser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getName&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG_MERGE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">equals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">childName&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// The &amp;lt;merge&amp;gt; tag doesn&amp;#39;t support android:theme, so&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// nothing special to do here.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">rInflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">childParser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childAttrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">createViewFromTag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childAttrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">hasThemeOverride&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TypedArray&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">obtainStyledAttributes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">styleable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Include&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getResourceId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">styleable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Include_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">NO_ID&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">visibility&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">styleable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Include_visibility&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recycle&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// We try to load the layout params set in the &amp;lt;include /&amp;gt; tag.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// If the parent can&amp;#39;t generate layout params (ex. missing width&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// or height for the framework ViewGroups, though this is not&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// necessarily true of all ViewGroups) then we expect it to throw&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// a runtime exception.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// We catch this exception and set localParams accordingly: true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// means we successfully loaded layout params from the &amp;lt;include&amp;gt;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// tag, false means we need to rely on the included layout params.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LayoutParams&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">generateLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RuntimeException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Ignore, just fail over to child attrs.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">generateLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">childAttrs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Inflate all children.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">rInflateChildren&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">childParser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childAttrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">NO_ID&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">switch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">visibility&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setVisibility&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">VISIBLE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setVisibility&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">INVISIBLE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">2&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setVisibility&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">GONE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">finally&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">childParser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">close&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>方法的参数有四个，分别是解析相关的&lt;code>parser&lt;/code>，&lt;code>Context&lt;/code>对象&lt;code>context&lt;/code>，父&lt;code>View&lt;/code> &lt;code>View&lt;/code>还有熟悉的&lt;code>AttributeSet&lt;/code>对象。只要有创建View的地方就有&lt;code>AttributeSet&lt;/code>对象存在，其作用是只有xml布局中的属性。&lt;/p>
&lt;p>再看方法内部，开始首先调用&lt;code>tryInflatePrecompiled&lt;/code>，我们前面已经知道这是单元测试用到的，这里返回的肯定是&lt;code>null&lt;/code>，再往下看。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG_MERGE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">equals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">childName&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="c1">// The &amp;lt;merge&amp;gt; tag doesn&amp;#39;t support android:theme, so&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// nothing special to do here.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">rInflate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">childParser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childAttrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里还是*&lt;code>TAG_MERGE&lt;/code>，不管它。*&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// else分支&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">createViewFromTag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childAttrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">hasThemeOverride&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TypedArray&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">obtainStyledAttributes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">styleable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Include&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getResourceId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">styleable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Include_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">NO_ID&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">visibility&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">styleable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Include_visibility&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recycle&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// We try to load the layout params set in the &amp;lt;include /&amp;gt; tag.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// If the parent can&amp;#39;t generate layout params (ex. missing width&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// or height for the framework ViewGroups, though this is not&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// necessarily true of all ViewGroups) then we expect it to throw&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// a runtime exception.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// We catch this exception and set localParams accordingly: true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// means we successfully loaded layout params from the &amp;lt;include&amp;gt;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// tag, false means we need to rely on the included layout params.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LayoutParams&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">generateLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RuntimeException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// Ignore, just fail over to child attrs.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">generateLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">childAttrs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// Inflate all children.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">rInflateChildren&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">childParser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">childAttrs&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">NO_ID&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">switch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">visibility&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setVisibility&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">VISIBLE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setVisibility&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">INVISIBLE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">2&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setVisibility&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">GONE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>else分支上来就创建了个View，还把&lt;code>parent&lt;/code>转成了&lt;code>ViewGroup&lt;/code>，把它添加进去了，不用头想也知道这个就是&lt;code>include&lt;/code>标签中的布局了。&lt;/p>
&lt;p>我们看到有个&lt;code>AttributeSet&lt;/code>参数&lt;code>childAttrs&lt;/code>，传入的要么是include标签的要么是被include的布局的。手写个demo，在被加载布局的构造方法打个断点，看到&lt;code>attrs&lt;/code>中的属性分明就是被include布局的。方法的参数&lt;code>attrs&lt;/code>肯定就是&lt;code>include&lt;/code>标签的属性了。&lt;/p>
&lt;p>这里有个疑问，怎么实现&lt;code>include&lt;/code>标签中有&lt;code>layout_width&lt;/code>和&lt;code>layout_height&lt;/code>时，属性是&lt;code>inlcude&lt;/code>标签上的呢？&lt;/p>
&lt;p>属性无非就两种来源，不是在xml布局中的设置的就是创建好View后设置的。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// We try to load the layout params set in the &amp;lt;include /&amp;gt; tag.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// If the parent can&amp;#39;t generate layout params (ex. missing width&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// or height for the framework ViewGroups, though this is not&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// necessarily true of all ViewGroups) then we expect it to throw&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// a runtime exception.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// We catch this exception and set localParams accordingly: true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// means we successfully loaded layout params from the &amp;lt;include&amp;gt;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// tag, false means we need to rely on the included layout params.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LayoutParams&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">generateLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RuntimeException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// Ignore, just fail over to child attrs.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">generateLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">childAttrs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>除了看到&lt;code>setLayoutParams&lt;/code>，注释也说的很清楚了。肯定就是我们所说的“创建好View后设置”的方法了。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Returns a new set of layout parameters based on the supplied attributes set.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @paramattrsthe attributes to build the layout parameters from
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @return an instance of {@linkandroid.view.ViewGroup.LayoutParams} or one
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * of its descendants
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LayoutParams&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">generateLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">AttributeSet&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">getContext&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attrs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>两个方法都是new一个新的&lt;code>LayoutParams&lt;/code>对象。子类重写方法，返回不同的类型。后面&lt;code>rInflateChildren&lt;/code>则是加载&lt;code>include&lt;/code>中的子&lt;code>View&lt;/code>和&lt;code>include&lt;/code>无关了。&lt;/p></description></item><item><title>SharedPreferences源码深度解析</title><link>https://opoojkk.com/posts/2023/10/10/sharedpreferences%E6%BA%90%E7%A0%81%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90/</link><pubDate>Tue, 10 Oct 2023 15:51:25 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/10/sharedpreferences%E6%BA%90%E7%A0%81%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90/</guid><description>&lt;h1 class="heading" id="sharedpreferences源码分析">
 SharedPreferences源码分析&lt;span class="heading__anchor"> &lt;a href="#sharedpreferences%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90">#&lt;/a>&lt;/span>
&lt;/h1>&lt;p>官方提供的保存键值对的方式，&lt;/p>
&lt;h2 class="heading" id="支持存储类型">
 &lt;strong>支持存储类型&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#%e6%94%af%e6%8c%81%e5%ad%98%e5%82%a8%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ol>
&lt;li>null&lt;/li>
&lt;li>String&lt;/li>
&lt;li>基本数据类型&lt;/li>
&lt;li>基本数据类型或String数组&lt;/li>
&lt;li>list&lt;/li>
&lt;li>map&lt;/li>
&lt;li>set&lt;/li>
&lt;/ol>
&lt;p>类型源码：XmlUtils#readThisValueXml&lt;/p>
&lt;h2 class="heading" id="文件实例">
 &lt;strong>文件实例&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#%e6%96%87%e4%bb%b6%e5%ae%9e%e4%be%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;figure>
 &lt;img src="https://i.imgur.com/3y0Oxuw.jpg">&lt;/figure>&lt;h2 class="heading" id="用法">
 &lt;strong>用法&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#%e7%94%a8%e6%b3%95">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 读&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getSharedPreferences&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MODE_PRIVATE&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">getString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;defValue&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 写&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getSharedPreferences&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MODE_PRIVATE&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">edit&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">putString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;key&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">commit&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getSharedPreferences&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MODE_PRIVATE&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">edit&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">putString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;key&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">apply&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="context">
 &lt;strong>Context&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#context">#&lt;/a>&lt;/span>
&lt;/h2>&lt;figure>
 &lt;img src="https://i.imgur.com/qULo9UK.jpg">&lt;/figure>&lt;h2 class="heading" id="countdownlatch用法示例">
 CountDownLatch用法示例&lt;span class="heading__anchor"> &lt;a href="#countdownlatch%e7%94%a8%e6%b3%95%e7%a4%ba%e4%be%8b">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// chatgpt生成&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">java.util.concurrent.CountDownLatch&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">MyThread&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Thread&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CountDownLatch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">latch&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">MyThread&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CountDownLatch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">latch&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">latch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">latch&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">run&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 执行一些操作&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 操作完成后，调用countDown()方法减少latch的计数器&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">latch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">countDown&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">MainActivity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AppCompatActivity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CountDownLatch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">latch&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">protected&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">onCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Bundle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">setContentView&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">activity_main&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">threadCount&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">3&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 设置线程数量&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 创建CountDownLatch，设置计数器为线程数量&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">latch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CountDownLatch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">threadCount&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 启动线程&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">threadCount&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">MyThread&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">thread&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MyThread&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">latch&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">thread&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">start&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 调用await()方法阻塞主线程，直到计数器变为0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">latch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">await&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 所有线程操作完成后，继续执行主线程的代码&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// ...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">InterruptedException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">printStackTrace&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="getsharedprefrences">
 &lt;strong>getSharedPrefrences&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#getsharedprefrences">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>从上面Context的结构可以看出，Context.getSharedPreferences都会调用到ContextImpl.getSharedPreferences，也就是：&lt;/p>
&lt;p>sSharedPrefsCache到mSharedPrefsPaths&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SharedPreferences&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getSharedPreferences&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mode&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">SharedPreferencesImpl&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ContextImpl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 		&lt;/span>&lt;span class="c1">// 缓存&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayMap&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SharedPreferencesImpl&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cache&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getSharedPreferencesCacheLocked&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cache&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 		&lt;/span>&lt;span class="c1">// 检查mode,不允许MODE_WORLD_READABLE或MODE_WORLD_WRITEABLE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">checkMode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mode&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">getApplicationInfo&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">targetSdkVersion&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">VERSION_CODES&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">O&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">isCredentialProtectedStorage&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">getSystemService&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">UserManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isUserUnlockingOrUnlocked&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">UserHandle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">myUserId&lt;/span>&lt;span class="p">()))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IllegalStateException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;SharedPreferences in credential encrypted &amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;storage are not available until after user is unlocked&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SharedPreferencesImpl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mode&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">cache&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">mode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MODE_MULTI_PROCESS&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">getApplicationInfo&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">targetSdkVersion&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">VERSION_CODES&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">HONEYCOMB&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// If somebody else (some other process) changed the prefs&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// file behind our back, we reload it. This has been the&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// historical (if undocumented) behavior.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">startReloadIfChangedUnexpectedly&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@GuardedBy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;ContextImpl.class&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayMap&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SharedPreferencesImpl&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getSharedPreferencesCacheLocked&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sSharedPrefsCache&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sSharedPrefsCache&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayMap&lt;/span>&lt;span class="o">&amp;lt;&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">packageName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getPackageName&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 以包名作为key从静态变量map中尝试取出SharedPreferencesImpl实例&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayMap&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SharedPreferencesImpl&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">packagePrefs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sSharedPrefsCache&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">packageName&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">packagePrefs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">packagePrefs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayMap&lt;/span>&lt;span class="o">&amp;lt;&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sSharedPrefsCache&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">packageName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">packagePrefs&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">packagePrefs&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>mode支持传入四种&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">MODE_PRIVATE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">MODE_WORLD_READABLE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">MODE_WORLD_WRITEABLE&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">MODE_MULTI_PROCESS&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>传入MODE_WORLD_READABLE或者MODE_WORLD_WRITEABLE抛出异常：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">checkMode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mode&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">getApplicationInfo&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">targetSdkVersion&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">VERSION_CODES&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">N&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">mode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MODE_WORLD_READABLE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SecurityException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;MODE_WORLD_READABLE no longer supported&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">mode&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MODE_WORLD_WRITEABLE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SecurityException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;MODE_WORLD_WRITEABLE no longer supported&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>MODE_MULTI_PROCESS注释中描述用于target sdk Android2.3以上，有些Android版本不生效且认为多进程不应该提供这种机制被弃用。&lt;/p>
&lt;p>总结:&lt;/p>
&lt;p>静态变量sSharedPrefsCache以包名做key&lt;/p>
&lt;p>private static ArrayMap&amp;lt;String, ArrayMap&amp;lt;File, SharedPreferencesImpl&amp;raquo; sSharedPrefsCache;&lt;/p>
&lt;p>ContextImpl成员变量mSharedPrefsPaths以&amp;quot;app路径/shared_prefs/name.xml&amp;quot;做key&lt;/p>
&lt;p>private ArrayMap&amp;lt;String, File&amp;gt; mSharedPrefsPaths;&lt;/p>
&lt;h2 class="heading" id="读">
 &lt;strong>读&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#%e8%af%bb">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>从读开始，既没有写复杂，又衔接上写部分的内容&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">SharedPreferencesImpl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">File&lt;/span> &lt;span class="k">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">int&lt;/span> &lt;span class="n">mode&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mFile&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">file&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mBackupFile&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">makeBackupFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">file&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mMode&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">mode&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mLoaded&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mMap&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mThrowable&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">startLoadFromDisk&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>涉及到的变量：&lt;/p>
&lt;p>mFile：sp对应xml文件&lt;/p>
&lt;p>mBackupFile：写入sp后保存&lt;/p>
&lt;p>mMode：模式，上面说过&lt;/p>
&lt;p>mLoaded：标识有没有加载过&lt;/p>
&lt;p>mMap：存储着要取出来的键值对&lt;/p>
&lt;p>mThrowable：保存从磁盘加载时的异常&lt;/p>
&lt;p>mLock：用来加锁的对象&lt;/p>
&lt;p>startLoadFromDisk：读取xml文件解析到mMap中，保存文件上次修改时间和文件大小&lt;/p>
&lt;p>所有类型流程都是类似的，解析xml文件得到类型，以String为例：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@Nullable&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nd">@Nullable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">defValue&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">awaitLoadedLocked&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">mMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">defValue&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>sp构造方法中异步读取xml文件并尝试解析，有可能get时还没有结束，这时要等到结束才能继续下一步(不然从mMap取不到)&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@GuardedBy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;mLock&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">awaitLoadedLocked&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">mLoaded&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Raise an explicit StrictMode onReadFromDisk for this&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// thread, since the real read will be in a different&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// thread and otherwise ignored by StrictMode.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">BlockGuard&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getThreadPolicy&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">onReadFromDisk&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">while&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">mLoaded&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mLock&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">wait&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">InterruptedException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">unused&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mThrowable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IllegalStateException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mThrowable&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 class="heading" id="写">
 &lt;strong>写&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#%e5%86%99">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>整理一下：&lt;/p>
&lt;ol>
&lt;li>mMap中存着所有的键值对&lt;/li>
&lt;/ol>
&lt;p>保存一个键值对时首先调用edit()方法：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Editor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">edit&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// TODO: remove the need to call awaitLoadedLocked() when&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// requesting an editor. will require some work on the&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Editor, but then we should be able to do:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">//&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// context.getSharedPreferences(..).edit().putString(..).apply()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">//&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// ... all without blocking.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">awaitLoadedLocked&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">EditorImpl&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>为什么要等到解析完了？先跳过，看后边。&lt;/p>
&lt;p>如果不需要等待解析，创建EditorImpl对象，EditorImpl中只有三个成员变量。分别是：&lt;/p>
&lt;ul>
&lt;li>mEditorLock：往map中添加时防止异步添加两个相同的键值对时有丢失&lt;/li>
&lt;li>mModified：需要修改的键值对&lt;/li>
&lt;li>mClear：表示有没有被清空&lt;/li>
&lt;/ul>
&lt;p>上面get是String，这里也对应看String：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Editor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">putString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nd">@Nullable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mEditorLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mModified&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>只是添加到mModified中，同时支持链式调用。既然这样，那实际的逻辑一定在commit和apply。&lt;/p>
&lt;h2 class="heading" id="commit">
 &lt;strong>commit&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#commit">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>先看commit：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">commit&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">long&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">startTime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DEBUG&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">startTime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">currentTimeMillis&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 返回了MemoryCommitResult对象&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">MemoryCommitResult&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">commitToMemory&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">//&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">SharedPreferencesImpl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">enqueueDiskWrite&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cm">/* sync write on this thread okay */&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 		&lt;/span>&lt;span class="c1">// 开始的CountDownLatch，要countdown后才能往下走&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">writtenToDiskLatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">await&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">InterruptedException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">finally&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DEBUG&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">d&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mFile&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getName&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;:&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">memoryStateGeneration&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; committed after &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">currentTimeMillis&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">startTime&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; ms&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// notify listeners&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">notifyListeners&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 返回结果&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">writeToDiskResult&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>为什么返回的是mcr.writeToDiskResult？&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MemoryCommitResult&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">commitToMemory&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">long&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">memoryStateGeneration&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">keysCleared&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">keysModified&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Set&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">OnSharedPreferenceChangeListener&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">listeners&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Map&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Object&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mapToWriteToDisk&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SharedPreferencesImpl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// We optimistically don&amp;#39;t make a deep copy until&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// a memory commit comes in when we&amp;#39;re already&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// writing to disk.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mDiskWritesInFlight&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// We can&amp;#39;t modify our mMap as a currently&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// in-flight write owns it. Clone it before&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// modifying it.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// noinspection unchecked&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mMap&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HashMap&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Object&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mMap&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mapToWriteToDisk&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mMap&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mDiskWritesInFlight&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">hasListeners&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mListeners&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">size&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hasListeners&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">keysModified&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayList&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">listeners&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HashSet&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">OnSharedPreferenceChangeListener&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mListeners&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">keySet&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mEditorLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">changesMade&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mClear&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">mapToWriteToDisk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isEmpty&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">changesMade&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mapToWriteToDisk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">clear&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">keysCleared&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mClear&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Map&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Entry&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Object&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mModified&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">entrySet&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getKey&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Object&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getValue&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// &amp;#34;this&amp;#34; is the magic value for a removal mutation. In addition,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// setting a value to &amp;#34;null&amp;#34; for a given key is specified to be&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// equivalent to calling remove on that key.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">mapToWriteToDisk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">containsKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mapToWriteToDisk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">remove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mapToWriteToDisk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">containsKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Object&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">existingValue&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mapToWriteToDisk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">existingValue&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">existingValue&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">equals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mapToWriteToDisk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">changesMade&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hasListeners&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">keysModified&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mModified&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">clear&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">changesMade&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mCurrentMemoryStateGeneration&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">memoryStateGeneration&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mCurrentMemoryStateGeneration&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MemoryCommitResult&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">memoryStateGeneration&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">keysCleared&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">keysModified&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">listeners&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mapToWriteToDisk&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>从这可以看出来：&lt;/p>
&lt;ol>
&lt;li>MemoryCommitResult内部的map是在mMap的基础上，过滤了与存在了的相同的键值对得来的。&lt;/li>
&lt;li>只有MemoryCommitResult#setDiskWriteResult中对writeToDiskResult赋值，所有调用处都是在实际的写入方法writeToFile中。&lt;/li>
&lt;li>setDiskWriteResult中调用writtenToDiskLatch.countDown()，commit方法可以继续往下执行。&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>第二条信息回应了上面为什么需要等到解析完成了再创建对象，写入时用到了这些已经存在的键值对。&lt;/strong>&lt;/p>
&lt;p>接着看加入队列，enqueueDiskWrite：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">enqueueDiskWrite&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MemoryCommitResult&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">postWriteRunnable&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 上面的第二个参数Runnable是null，这种情况都是commit来的&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">isFromSyncCommit&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">postWriteRunnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">writeToDiskRunnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">run&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mWritingToDiskLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 		&lt;/span>&lt;span class="c1">// 真正的写入逻辑&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">writeToFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">isFromSyncCommit&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mDiskWritesInFlight&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">postWriteRunnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">postWriteRunnable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 唯一需要写入的，直接run方法不会创建新线程，这时候在主线程写入。&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">isFromSyncCommit&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">wasEmpty&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">wasEmpty&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mDiskWritesInFlight&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">wasEmpty&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">writeToDiskRunnable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 加入队列，异步写入&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">QueuedWork&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">queue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">writeToDiskRunnable&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">isFromSyncCommit&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>除开在主线程写入的情况，其他都需要添加到QueuedWork中：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Queue a work-runnable for processing asynchronously.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param work The new runnable to process
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param shouldDelay If the message should be delayed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@UnsupportedAppUsage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">maxTargetSdk&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">VERSION_CODES&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">R&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">trackingBug&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">170729553&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">queue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">work&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">shouldDelay&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 	&lt;/span>&lt;span class="c1">// 返回的对象是一个handler,创建对象的参数Loop是handlerThread创建的子线程的。&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Handler&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handler&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getHandler&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 	&lt;/span>&lt;span class="c1">// 加到链表&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sWork&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">work&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 	&lt;/span>&lt;span class="c1">// 发送消息，sCanDelay留坑&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">shouldDelay&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sCanDelay&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">handler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">sendEmptyMessageDelayed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">QueuedWorkHandler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MSG_RUN&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DELAY&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 		&lt;/span>&lt;span class="c1">// commit一定走这里&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">handler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">sendEmptyMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">QueuedWorkHandler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MSG_RUN&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>MSG_RUN消息：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">handleMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Message&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">what&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MSG_RUN&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">processPendingWork&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>执行任务：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">processPendingWork&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">long&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">startTime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DEBUG&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">startTime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">currentTimeMillis&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sProcessingWork&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">LinkedList&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">work&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">work&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sWork&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sWork&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LinkedList&lt;/span>&lt;span class="o">&amp;lt;&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Remove all msg-s as all work will be processed now&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">getHandler&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">removeMessages&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">QueuedWorkHandler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MSG_RUN&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">work&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">size&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">w&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">work&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 	&lt;/span>&lt;span class="c1">// 虽然是调用run，运行确实在子线程。&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">w&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DEBUG&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">d&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOG_TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;processing &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">work&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">size&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; items took &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">currentTimeMillis&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">startTime&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; ms&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>运行了，追到：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">writeToDiskRunnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">run&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mWritingToDiskLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">writeToFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">isFromSyncCommit&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">synchronized&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mLock&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mDiskWritesInFlight&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">postWriteRunnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">postWriteRunnable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;span class="lnt">124
&lt;/span>&lt;span class="lnt">125
&lt;/span>&lt;span class="lnt">126
&lt;/span>&lt;span class="lnt">127
&lt;/span>&lt;span class="lnt">128
&lt;/span>&lt;span class="lnt">129
&lt;/span>&lt;span class="lnt">130
&lt;/span>&lt;span class="lnt">131
&lt;/span>&lt;span class="lnt">132
&lt;/span>&lt;span class="lnt">133
&lt;/span>&lt;span class="lnt">134
&lt;/span>&lt;span class="lnt">135
&lt;/span>&lt;span class="lnt">136
&lt;/span>&lt;span class="lnt">137
&lt;/span>&lt;span class="lnt">138
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> @GuardedBy(&amp;#34;mWritingToDiskLock&amp;#34;)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> private void writeToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 	// ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> boolean fileExists = mFile.exists();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // Rename the current file so it may be used as a backup during the next read
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (fileExists) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> boolean needsWrite = false;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // Only need to write if the disk state is older than this commit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 	// 前提条件是Editor中mModified变化，与MemoryCommitResult创建时不一致。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (mDiskStateGeneration &amp;lt; mcr.memoryStateGeneration) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (isFromSyncCommit) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> needsWrite = true;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> } else {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> synchronized (mLock) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // No need to persist intermediate states. Just wait for the latest state to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // be persisted.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (mCurrentMemoryStateGeneration == mcr.memoryStateGeneration) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> needsWrite = true;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (!needsWrite) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mcr.setDiskWriteResult(false, true);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> return;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> boolean backupFileExists = mBackupFile.exists();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (DEBUG) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> backupExistsTime = System.currentTimeMillis();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (!backupFileExists) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (!mFile.renameTo(mBackupFile)) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Log.e(TAG, &amp;#34;Couldn&amp;#39;t rename file &amp;#34; + mFile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> + &amp;#34; to backup file &amp;#34; + mBackupFile);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mcr.setDiskWriteResult(false, false);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> return;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> } else {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mFile.delete();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // Attempt to write the file, delete the backup and return true as atomically as
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // possible. If any exception occurs, delete the new file; next time we will restore
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // from the backup.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> try {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> FileOutputStream str = createFileOutputStream(mFile);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (DEBUG) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> outputStreamCreateTime = System.currentTimeMillis();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (str == null) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mcr.setDiskWriteResult(false, false);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> return;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> writeTime = System.currentTimeMillis();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> FileUtils.sync(str);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> fsyncTime = System.currentTimeMillis();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> str.close();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (DEBUG) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> setPermTime = System.currentTimeMillis();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> try {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> final StructStat stat = Os.stat(mFile.getPath());
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> synchronized (mLock) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mStatTimestamp = stat.st_mtim;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mStatSize = stat.st_size;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> } catch (ErrnoException e) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // Do nothing
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (DEBUG) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> fstatTime = System.currentTimeMillis();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // Writing was successful, delete the backup file if there is one.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mBackupFile.delete();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (DEBUG) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> deleteTime = System.currentTimeMillis();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mDiskStateGeneration = mcr.memoryStateGeneration;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mcr.setDiskWriteResult(true, true);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (DEBUG) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Log.d(TAG, &amp;#34;write: &amp;#34; + (existsTime - startTime) + &amp;#34;/&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> + (backupExistsTime - startTime) + &amp;#34;/&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> + (outputStreamCreateTime - startTime) + &amp;#34;/&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> + (writeTime - startTime) + &amp;#34;/&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> + (fsyncTime - startTime) + &amp;#34;/&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> + (setPermTime - startTime) + &amp;#34;/&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> + (fstatTime - startTime) + &amp;#34;/&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> + (deleteTime - startTime));
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> long fsyncDuration = fsyncTime - writeTime;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mSyncTimes.add((int) fsyncDuration);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mNumSync++;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (DEBUG || mNumSync % 1024 == 0 || fsyncDuration &amp;gt; MAX_FSYNC_DURATION_MILLIS) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mSyncTimes.log(TAG, &amp;#34;Time required to fsync &amp;#34; + mFile + &amp;#34;: &amp;#34;);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> return;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> } catch (XmlPullParserException e) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Log.w(TAG, &amp;#34;writeToFile: Got exception:&amp;#34;, e);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> } catch (IOException e) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Log.w(TAG, &amp;#34;writeToFile: Got exception:&amp;#34;, e);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // Clean up an unsuccessfully written file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (mFile.exists()) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (!mFile.delete()) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Log.e(TAG, &amp;#34;Couldn&amp;#39;t clean up partially-written file &amp;#34; + mFile);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mcr.setDiskWriteResult(false, false);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>writeToFile主要做了几件事：&lt;/p>
&lt;ol>
&lt;li>把map转化成xml格式，写入&lt;/li>
&lt;li>更新mStatTimestamp和mStatSize状态&lt;/li>
&lt;li>调用setDiskWriteResult，更新了写入文件结果同时commit中继续执行，能执行下一次提交。&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="apply">
 &lt;strong>apply&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#apply">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>相较commit，enqueueDiskWrite传入了第二个参数postWriteRunnable，其他部分相同。postWriteRunnable对应代码：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">awaitCommit&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">run&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">writtenToDiskLatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">await&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">InterruptedException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ignored&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DEBUG&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">wasWritten&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">d&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TAG&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mFile&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getName&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;:&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mcr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">memoryStateGeneration&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; applied after &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">currentTimeMillis&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">startTime&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34; ms&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">QueuedWork&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addFinisher&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">awaitCommit&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">postWriteRunnable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">run&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">awaitCommit&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">QueuedWork&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">removeFinisher&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">awaitCommit&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Runnable不为null，isFromSyncCommit为false，所有的任务都要到子线程执行。&lt;/p>
&lt;p>QueuedWork#queue中shouldDelay为true，当shouldDelay和sCanDelay同时为true执行&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">handler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">sendEmptyMessageDelayed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">QueuedWorkHandler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MSG_RUN&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DELAY&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>sCanDelay的初始值就是true，改变它的值都在waitToFinish方法中，发现是找不得调用的。官方在注释中给我们的解释：
1)&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Trigger queued work to be processed immediately. The queued work is processed on a separate
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * thread asynchronous. While doing that run and process all finishers on this thread. The
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * finishers can be implemented in a way to check weather the queued work is finished.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Is called from the Activity base class&amp;#39;s onPause(), after BroadcastReceiver&amp;#39;s onReceive,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * after Service command handling, etc. (so async work is never lost)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>防止丢失异步任务。&lt;/p>
&lt;p>开始遍历执行任务时赋值false，这时apply发送消息的不会延迟，执行结束后赋值true，又会延迟。为什么会反常呢？&lt;/p>
&lt;p>waitToFinish是要防止丢失任务，在保存的时候来了一条新的任务，执行了才能存下来这次的数据。&lt;/p>
&lt;h2 class="heading" id="anr原因">
 anr原因&lt;span class="heading__anchor"> &lt;a href="#anr%e5%8e%9f%e5%9b%a0">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>&lt;a href="https://mp.weixin.qq.com/s?__biz=MzI1MzYzMjE0MQ==&amp;mid=2247484387&amp;idx=1&amp;sn=e3c8d6ef52520c51b5e07306d9750e70&amp;scene=21">剖析 SharedPreference apply 引起的 ANR 问题 - 字节跳动团队&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://juejin.cn/post/6844904033820377096">SharedPreferences ANR问题分析 &amp;amp; Android8.0的优化&lt;/a>&lt;/p></description></item><item><title>Android library模块publish到JitPack完成流程</title><link>https://opoojkk.com/posts/2023/10/06/android-gradle-library-publish-to-jitpack/</link><pubDate>Fri, 06 Oct 2023 14:29:46 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/06/android-gradle-library-publish-to-jitpack/</guid><description>&lt;h2 class="heading" id="背景">
 背景&lt;span class="heading__anchor"> &lt;a href="#%e8%83%8c%e6%99%af">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>前段时间想要找一个支持Material You主题的FloatingActionButton，几经辗转总算是找到了，但组件是在另一个开源库的基础上封装继承，一直拖到昨天才把该部分抽离出来，放到了&lt;a href="https://github.com/opoojkk/material-speed-dial">单独项目&lt;/a>中。&lt;/p>
&lt;p>由于View依赖了几个类，每个类都有各自的分工，聚合到一起会导致View类过大，且有应该它承担的职责，故没有将所有方法放到一个类文件中。
这样麻烦就到了引用时，昨天睡前决定以开源库的形式支持，于是有了这篇文章。&lt;/p>
&lt;h2 class="heading" id="前置知识">
 前置知识&lt;span class="heading__anchor"> &lt;a href="#%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%af%86">#&lt;/a>&lt;/span>
&lt;/h2>&lt;h3 class="heading" id="android-module类型">
 Android module类型&lt;span class="heading__anchor"> &lt;a href="#android-module%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Android中module分为两类，分别是application和library，application是可以运行起来的app，library则是某一部分独立的功能。library又可以细分为Java library和Android library，两者的区别在于Java library比Android library少了资源文件（项目res文件夹下），构建出来的是jar；Android library构建出来的是aar。&lt;/p>
&lt;h3 class="heading" id="jitpack构建流程">
 JitPack构建流程&lt;span class="heading__anchor"> &lt;a href="#jitpack%e6%9e%84%e5%bb%ba%e6%b5%81%e7%a8%8b">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>先check out出你的代码，执行Gradle task：&lt;code>publishToMavenLocal&lt;/code>，再把生成的aar文件上传以&lt;code>com.github.用户名.仓库名称:tag&lt;/code>的格式。这些都由JitPack帮我们完成。&lt;/p>
&lt;h2 class="heading" id="行动">
 行动&lt;span class="heading__anchor"> &lt;a href="#%e8%a1%8c%e5%8a%a8">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>决定了publish到开源仓库，接下来要决定到哪个仓库。常用的几个分别有：&lt;/p>
&lt;ul>
&lt;li>Google&lt;/li>
&lt;li>MavenCenter&lt;/li>
&lt;li>JetCenter(停止支持)&lt;/li>
&lt;li>JitPack&lt;/li>
&lt;/ul>
&lt;p>JetCenter从去年开始宣布停止维护，首先排除。
Google没有找到url等相关信息，加上平时用到Google仓库的都是官方提供的依赖，猜测不开放给publish。
剩下的两个MavenCenter和JitPack。JitPack依赖git，Github仓库的中打tag就可以创建，最易操作，但以来的group和artifact是不能修改的。MavenCenter搜索到的步骤很复杂，日后再说。&lt;/p>
&lt;p>于是最后决定用JitPack。&lt;/p>
&lt;h3 class="heading" id="项目配置">
 项目配置&lt;span class="heading__anchor"> &lt;a href="#%e9%a1%b9%e7%9b%ae%e9%85%8d%e7%bd%ae">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Android中module分为两类，分别是application和library，application是可以运行起来的app，library则是某一部分独立的功能。library又可以细分为Java library和Android library，两者的区别在于Java library比Android library少了资源文件（项目res文件夹下），构建出来的是jar文件；Android library构建出来的是aar文件。&lt;/p>
&lt;h4 class="heading" id="添加gradle-plugin并配置依赖库">
 添加Gradle Plugin并配置依赖库&lt;span class="heading__anchor"> &lt;a href="#%e6%b7%bb%e5%8a%a0gradle-plugin%e5%b9%b6%e9%85%8d%e7%bd%ae%e4%be%9d%e8%b5%96%e5%ba%93">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>创建一个依赖库需要用到library module。创建一个module，Templates选&lt;code>Android Library&lt;/code>，输入名称，创建。
然后就是等待构建，构建完成后在刚刚module的build.gradle（Kotlin版本对应为buil.gradle.kts）增加Gradle Plugin：&lt;code>maven-publish&lt;/code>。&lt;/p>
&lt;blockquote>
&lt;p>最好是添加依赖后先同步，等同步完成再添加publishing代码块，否则publishing代码块会报红。&lt;/p>
&lt;/blockquote>
&lt;p>在build.gradle文件的最外层与（与plugins、android、dependencies等通层级）添加：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">publishing&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">publications&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">MavenPublication&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;maven&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">groupId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;io.github.opoojkk&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">artifactId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;material-speed-dial&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">version&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;1.0.0&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">afterEvaluate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">artifact&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tasks&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getByName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;bundleReleaseAar&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>groupId&lt;/code>和&lt;code>artifactId&lt;/code>都修改成自己的（尝试后发现JitPack中修改了也还是固定的com.github.用户名.仓库名:tag的形式😅）。&lt;/p>
&lt;p>另外，JitPack官方文档中用的是 &lt;code>from(components[&amp;quot;java&amp;quot;])&lt;/code>，这样会提示&lt;code>SoftwareComponentInternal with name 'java' not found.&lt;/code>。原因暂时不详，参考链接：&lt;a href="https://stackoverflow.com/questions/62982138/softwarecomponentinternal-with-name-java-not-found">SoftwareComponentInternal with name &amp;lsquo;java&amp;rsquo; not found&lt;/a>&lt;/p>
&lt;h4 class="heading" id="匹配gradle和target-java">
 匹配Gradle和Target Java&lt;span class="heading__anchor"> &lt;a href="#%e5%8c%b9%e9%85%8dgradle%e5%92%8ctarget-java">#&lt;/a>&lt;/span>
&lt;/h4>&lt;p>踩过的坑：
项目Gradle版本8.0，Java设置1.8，疯狂提示：
No matching variant of com.android.tools.build:gradle:8.1.1 was found.
改成Java 11后提示要用18，再改成18搞定。&lt;/p>
&lt;p>&lt;strong>&lt;code>sourceCompatibility&lt;/code>、&lt;code>targetCompatibility&lt;/code>和&lt;code>jvmTarget&lt;/code>后的值都需要统一修改。&lt;/strong>&lt;/p>
&lt;h3 class="heading" id="让依赖库可以被依赖">
 让依赖库可以被依赖&lt;span class="heading__anchor"> &lt;a href="#%e8%ae%a9%e4%be%9d%e8%b5%96%e5%ba%93%e5%8f%af%e4%bb%a5%e8%a2%ab%e4%be%9d%e8%b5%96">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>开始时说过是依赖Github仓库，因此这一步需要将仓库推送到Github，步骤略去。
推送后在仓库中点击&lt;code>Release&lt;/code>
&lt;img src="https://i.imgur.com/qCsGI9e.jpg">
&lt;code>Draft a new release&lt;/code>
&lt;img src="https://i.imgur.com/cCpAbGJ.jpg">
选择Choose a tag，输入一个tag，这里是tag会对应到依赖库的版本，可以输入&lt;code>1.0.0&lt;/code>这样的格式，其他当然也可以。要点击&lt;code>Create new tag: xxx on publish&lt;/code>，Release title也填入&lt;code>1.0.0&lt;/code>。
这样完成了Github部分的步骤。&lt;/p>
&lt;p>接下来打开&lt;a href="https://jitpack.io/">JitPack首页&lt;/a>，把刚刚的Github仓库地址复制下来，粘贴到搜索框点击&lt;code>Look up&lt;/code>，就可以看到这样的ui：
&lt;img src="https://i.imgur.com/pZUYaWe.jpg">&lt;/p>
&lt;p>想要得到依赖库，需要点击&lt;code>Get it&lt;/code>。&lt;/p>
&lt;p>刚刚的部分会显示这样的状态，标识正在构建：
&lt;img src="https://i.imgur.com/oWOmE8n.jpg">&lt;/p>
&lt;p>完成后前面多出一个绿色的icon，表示已经成功：
&lt;img src="https://i.imgur.com/A7sDOmr.jpg">
点击可以见到log，在log的最后能够看到依赖库的地址。&lt;/p>
&lt;p>当红色的icon时表示出了问题，可以点击红色的icon查看问题的原因。
再然后就是导入了，可以参考&lt;a href="https://docs.jitpack.io/android/#gradle">官网文档&lt;/a>。&lt;/p>
&lt;h2 class="heading" id="总结">
 总结：&lt;span class="heading__anchor"> &lt;a href="#%e6%80%bb%e7%bb%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>总结一下步骤：&lt;/p>
&lt;ol>
&lt;li>Android项目中创建module，添加插件并配置；&lt;/li>
&lt;li>上传到Github仓库；&lt;/li>
&lt;li>Github仓库打tag；&lt;/li>
&lt;li>到JitPack找到仓库，点击Get it得到依赖库。&lt;/li>
&lt;/ol>
&lt;h2 class="heading" id="参考链接">
 参考链接&lt;span class="heading__anchor"> &lt;a href="#%e5%8f%82%e8%80%83%e9%93%be%e6%8e%a5">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ol>
&lt;li>&lt;a href="https://stackoverflow.com/questions/62982138/softwarecomponentinternal-with-name-java-not-found">SoftwareComponentInternal with name &amp;lsquo;java&amp;rsquo; not found&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.jitpack.io/android/#gradle">JitPack文档&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>Android Studio代理配置指南</title><link>https://opoojkk.com/posts/2023/10/05/android-studio%E4%BB%A3%E7%90%86%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97/</link><pubDate>Thu, 05 Oct 2023 14:52:09 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/05/android-studio%E4%BB%A3%E7%90%86%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97/</guid><description>&lt;p>平时为了速度总是习惯给Android Studio设置代理，通过Android Studio的网络请求都走代理。代理总是抽风，代理设置总在&amp;quot;auto-detect proxy settings&amp;quot;和&amp;quot;Manual proxy configuration&amp;quot;之间横跳。还真不知道代理到底用的是哪个，只要能正常拉下来依赖，用哪个不重要。&lt;/p>
&lt;p>但今天，不一样。
今天心血来潮，想要创建一个新Android项目，因为Android Studio已经升级到了最新版，因此已经将Gradle默认语言设置为了kts，kts就kts，Kotlin我还熟悉点。
万万没想到项目创建完成构建时出了岔子，突然来了个提示：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Plugin [id: &amp;#39;com.android.application&amp;#39;, version: &amp;#39;8.1.1&amp;#39;, apply: false] was not found in any of the following sources:
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>嗯？难不成是kts的问题，赶紧换回groovy，还治不了你。
再次运行，还是这个鸟样&amp;hellip;.&lt;/p>
&lt;p>行，错怪kts了，我再改回kts嘿嘿☺️。&lt;/p>
&lt;p>打开Android Studio代理设置就看到提示说Gradle设置了代理。要设置了我还不知道吗？准是Gradle搞的鬼。&lt;/p>
&lt;p>打开终端，输入&lt;code>cd .gralde&lt;/code>，再输&lt;code>open .&lt;/code>，再用vs code打开gradle.properties文件，一通操作行云流水，轻车熟路（天知道被折磨过多少回了）。果然里边写着代理配置：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">systemProp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">http&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">proxyHost&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">127&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">0&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">0&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">systemProp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">http&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">proxyPort&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">7890&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">systemProp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">https&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">proxyHost&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">127&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">0&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">0&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">systemProp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">https&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">proxyPort&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">7890&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>四行全都给他扬了，关掉重新打开Android Studio，搞定！打完收工。&lt;/p>
&lt;p>&lt;strong>补充：&lt;/strong>
Gradle的全局代理文件路径是固定的即&lt;code>~/.gradle/gradle.properties&lt;/code>，怎么删掉最后几行就随意了。
用vim操作：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>打开文件&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">vim ~/.gradle/gradle.properties
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>进入普通模式&lt;/p>
&lt;p>点击ESC键&lt;/p>
&lt;/li>
&lt;li>
&lt;p>删除最后四行的内容&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">:-4,&lt;span class="nv">$d&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>保存&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">:wq
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>点击Enter键，重启Android Studio，搞定&lt;/p>
&lt;/li>
&lt;/ol></description></item><item><title>Linux文件系统权限管理</title><link>https://opoojkk.com/posts/2023/10/05/linux%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/</link><pubDate>Thu, 05 Oct 2023 14:33:19 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/05/linux%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/</guid><description>&lt;h2 class="heading" id="权限">
 权限&lt;span class="heading__anchor"> &lt;a href="#%e6%9d%83%e9%99%90">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Linux 系统是一种典型的多用户系统，不同的用户处于不同的地位，拥有不同的权限。&lt;/p>
&lt;p>为了保护系统的安全性，Linux 系统对不同的用户访问同一文件（包括目录文件）的权限做了不同的规定。&lt;/p>
&lt;p>常用的命令ls可以简单的列出所有不隐藏的文件，&lt;code>ls -l&lt;/code>使用长格式列出文件及目录的详细信息。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-verilog" data-lang="verilog">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="p">@&lt;/span>&lt;span class="n">www&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="p">]#&lt;/span> &lt;span class="n">ls&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">l&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">total&lt;/span> &lt;span class="mh">64&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">xr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">xr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="mh">2&lt;/span> &lt;span class="n">root&lt;/span> &lt;span class="n">root&lt;/span> &lt;span class="mh">4096&lt;/span> &lt;span class="n">Dec&lt;/span> &lt;span class="mh">14&lt;/span> &lt;span class="mh">2012&lt;/span> &lt;span class="n">bin&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">xr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">xr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="mh">4&lt;/span> &lt;span class="n">root&lt;/span> &lt;span class="n">root&lt;/span> &lt;span class="mh">4096&lt;/span> &lt;span class="n">Apr&lt;/span> &lt;span class="mh">19&lt;/span> &lt;span class="mh">2012&lt;/span> &lt;span class="n">boot&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">……&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>实例中，&lt;strong>bin&lt;/strong> 文件的第一个属性用 &lt;strong>d&lt;/strong> 表示。&lt;strong>d&lt;/strong> 在 Linux 中代表该文件是一个目录文件。&lt;/p>
&lt;p>在 Linux 中第一个字符代表这个文件是目录、文件或链接文件等等。&lt;/p>
&lt;ul>
&lt;li>当为 &lt;strong>d&lt;/strong> 则是目录&lt;/li>
&lt;li>当为 B**** 则是文件；&lt;/li>
&lt;li>若是 &lt;strong>l&lt;/strong> 则表示为链接文档(link file)；&lt;/li>
&lt;li>若是 &lt;strong>b&lt;/strong> 则表示为装置文件里面的可供储存的接口设备(可随机存取装置)；&lt;/li>
&lt;li>若是 &lt;strong>c&lt;/strong> 则表示为装置文件里面的串行端口设备，例如键盘、鼠标(一次性读取装置)。&lt;/li>
&lt;/ul>
&lt;p>接下来的字符中，以三个为一组，且均为 &lt;strong>rwx&lt;/strong> 的三个参数的组合。其中， &lt;strong>r&lt;/strong> 代表可读(read)、 &lt;strong>w&lt;/strong> 代表可写(write)、 &lt;strong>x&lt;/strong> 代表可执行(execute)。 要注意的是，这三个权限的位置不会改变，如果没有权限，就会出现减号 &lt;strong>-&lt;/strong> 而已。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/zJpDRDf.jpg">&lt;/figure>&lt;p>每个文件的属性由左边第一部分的 10 个字符来确定（如下图）。&lt;/p>
&lt;figure>
 &lt;img src="https://i.imgur.com/yNFeTIY.jpg">&lt;/figure>&lt;p>从左至右用 &lt;strong>0-9&lt;/strong> 这些数字来表示。&lt;/p>
&lt;p>第 &lt;strong>0&lt;/strong> 位确定文件类型，第 &lt;strong>1-3&lt;/strong> 位确定属主（该文件的所有者）拥有该文件的权限。&lt;/p>
&lt;p>第4-6位确定属组（所有者的同组用户）拥有该文件的权限，第7-9位确定其他用户拥有该文件的权限。&lt;/p>
&lt;p>其中，第 &lt;strong>1、4、7&lt;/strong> 位表示读权限，如果用 &lt;strong>r&lt;/strong> 字符表示，则有读权限，如果用 &lt;strong>-&lt;/strong> 字符表示，则没有读权限；&lt;/p>
&lt;p>第 &lt;strong>2、5、8&lt;/strong> 位表示写权限，如果用 &lt;strong>w&lt;/strong> 字符表示，则有写权限，如果用 &lt;strong>-&lt;/strong> 字符表示没有写权限；第 &lt;strong>3、6、9&lt;/strong> 位表示可执行权限，如果用 &lt;strong>x&lt;/strong> 字符表示，则有执行权限，如果用 &lt;strong>-&lt;/strong> 字符表示，则没有执行权限。&lt;/p>
&lt;h2 class="heading" id="linux文件属主和属组">
 Linux文件属主和属组&lt;span class="heading__anchor"> &lt;a href="#linux%e6%96%87%e4%bb%b6%e5%b1%9e%e4%b8%bb%e5%92%8c%e5%b1%9e%e7%bb%84">#&lt;/a>&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-prolog" data-lang="prolog">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="s">root@www&lt;/span> &lt;span class="s">/&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="s">#&lt;/span> &lt;span class="s">ls&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="s">l&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">total&lt;/span> &lt;span class="mi">64&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">drwxr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="s">xr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="s">x&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="s">root&lt;/span> &lt;span class="s">root&lt;/span> &lt;span class="mi">4096&lt;/span> &lt;span class="nv">Feb&lt;/span> &lt;span class="mi">15&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="s">:&lt;/span>&lt;span class="mi">46&lt;/span> &lt;span class="s">cron&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">drwxr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="s">xr&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="s">x&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="s">mysql&lt;/span> &lt;span class="s">mysql&lt;/span> &lt;span class="mi">4096&lt;/span> &lt;span class="nv">Apr&lt;/span> &lt;span class="mi">21&lt;/span> &lt;span class="mi">2014&lt;/span> &lt;span class="s">mysql&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">……&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>对于文件来说，它都有一个特定的所有者，也就是对该文件具有所有权的用户。&lt;/p>
&lt;blockquote>
&lt;p>多用户系统，所以每个文件是属于某一个用户的。&lt;/p>
&lt;/blockquote>
&lt;p>同时，在Linux系统中，用户是按组分类的，一个用户属于一个或多个组。&lt;/p>
&lt;p>文件所有者以外的用户又可以分为文件所属组的同组用户和其他用户。&lt;/p>
&lt;p>因此，Linux系统按文件所有者、文件所有者同组用户和其他用户来规定了不同的文件访问权限。&lt;/p>
&lt;p>在以上实例中，mysql 文件是一个目录文件，属主和属组都为 mysql，属主有可读、可写、可执行的权限；与属主同组的其他用户有可读和可执行的权限；其他用户也有可读和可执行的权限。&lt;/p>
&lt;p>对于 root 用户来说，一般情况下，文件的权限对其不起作用。&lt;/p>
&lt;p>&lt;strong>更改文件属性&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>chgrp：更改文件属组&lt;/li>
&lt;li>chown：更改文件属主，也可以同时更改文件属组&lt;/li>
&lt;li>chmod：更改文件9个属性&lt;/li>
&lt;/ol>
&lt;p>ch → change&lt;/p>
&lt;p>grp → group&lt;/p>
&lt;p>own → own&lt;/p>
&lt;p>mod → mode&lt;/p>
&lt;p>其中常用的无疑是chmod，&lt;/p>
&lt;p>Linux文件属性有两种设置方法，一种是数字，一种是符号。&lt;/p>
&lt;p>Linux 文件的基本权限就有九个，分别是 &lt;strong>owner/group/others(拥有者/组/其他)&lt;/strong> 三种身份各有自己的 &lt;strong>read/write/execute&lt;/strong> 权限。&lt;/p>
&lt;p>先复习一下刚刚上面提到的数据：文件的权限字符为： &lt;strong>-rwxrwxrwx&lt;/strong> ， 这九个权限是三个三个一组的！其中，我们可以使用数字来代表各个权限，各权限的分数对照表如下：&lt;/p>
&lt;ul>
&lt;li>r:4 → 2^2&lt;/li>
&lt;li>w:2 → 2^1&lt;/li>
&lt;li>x:1 → 2^0&lt;/li>
&lt;/ul>
&lt;p>每种身份(owner/group/others)各自的三个权限(r/w/x)分数是需要累加的，例如当权限为： &lt;strong>-rwxrwx&amp;mdash;&lt;/strong> 分数则是：&lt;/p>
&lt;ul>
&lt;li>owner = rwx = 4+2+1 = 7&lt;/li>
&lt;li>group = rwx = 4+2+1 = 7&lt;/li>
&lt;li>others= &amp;mdash; = 0+0+0 = 0&lt;/li>
&lt;/ul>
&lt;p>所以等一下我们设定权限的变更时，该文件的权限数字就是 &lt;strong>770&lt;/strong>。变更权限的指令 chmod 的语法是这样的：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> chmod [-R] xyz 文件或目录
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>选项与参数：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>xyz&lt;/strong> : 就是刚刚提到的数字类型的权限属性，为 &lt;strong>rwx&lt;/strong> 属性数值的相加。&lt;/li>
&lt;li>&lt;strong>R&lt;/strong> : 进行递归(recursive)的持续变更，以及连同次目录下的所有文件都会变更&lt;/li>
&lt;/ul>
&lt;p>举例来说，如果要将 &lt;strong>.bashrc&lt;/strong> 这个文件所有的权限都设定启用，那么命令如下：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[root@www ~]# ls -al .bashrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rw-r--r-- 1 root root 395 Jul 4 11:45 .bashrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[root@www ~]# chmod 777 .bashrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[root@www ~]# ls -al .bashrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rwxrwxrwx 1 root root 395 Jul 4 11:45 .bashrc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>那如果要将权限变成 &lt;em>-rwxr-xr&amp;ndash;&lt;/em> 呢？那么权限的分数就成为 [4+2+1][4+0+1][4+0+0]=754。&lt;/p>
&lt;h3 class="heading" id="符号类型改变文件权限">
 &lt;strong>符号类型改变文件权限&lt;/strong>&lt;span class="heading__anchor"> &lt;a href="#%e7%ac%a6%e5%8f%b7%e7%b1%bb%e5%9e%8b%e6%94%b9%e5%8f%98%e6%96%87%e4%bb%b6%e6%9d%83%e9%99%90">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>还有一个改变权限的方法，从之前的介绍中我们可以发现，基本上就九个权限分别是：&lt;/p>
&lt;ul>
&lt;li>user：用户&lt;/li>
&lt;li>group：组&lt;/li>
&lt;li>others：其他&lt;/li>
&lt;/ul>
&lt;p>那么我们就可以使用 &lt;strong>u, g, o&lt;/strong> 来代表三种身份的权限。&lt;/p>
&lt;p>此外， &lt;strong>a&lt;/strong> 则代表 &lt;strong>all&lt;/strong>，即全部的身份。读写的权限可以写成 &lt;strong>r, w, x&lt;/strong>，也就是可以使用下表的方式来看：&lt;/p>
&lt;p>如果我们需要将文件权限设置为 &lt;strong>-rwxr-xr&amp;ndash;&lt;/strong> ，可以使用 &lt;strong>chmod u=rwx,g=rx,o=r 文件名&lt;/strong> 来设定:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># touch test1 // 创建 test1 文件
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># ls -al test1 // 查看 test1 默认权限
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rw-r--r-- 1 root root 0 Nov 15 10:32 test1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># chmod u=rwx,g=rx,o=r test1 // 修改 test1 权限
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># ls -al test1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rwxr-xr-- 1 root root 0 Nov 15 10:32 test1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>而如果是要将权限去掉而不改变其他已存在的权限呢？例如要拿掉全部人的可执行权限，则：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># chmod a-x test1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># ls -al test1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rw-r--r-- 1 root root 0 Nov 15 10:32 test1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>About</title><link>https://opoojkk.com/about/</link><pubDate>Thu, 05 Oct 2023 10:22:30 +0800</pubDate><guid>https://opoojkk.com/about/</guid><description>&lt;h2 class="heading" id="关于我">
 关于我&lt;span class="heading__anchor"> &lt;a href="#%e5%85%b3%e4%ba%8e%e6%88%91">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>世界主义&lt;/li>
&lt;li>喜欢怀疑&lt;/li>
&lt;li>对世界充满好奇心&lt;/li>
&lt;li>喜欢哲学、逻辑学&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="coding">
 Coding&lt;span class="heading__anchor"> &lt;a href="#coding">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>21年本科毕业，工作四年有余的Android开发工程师，工作中使用Java和Kotlin两种语言(19开)&lt;/li>
&lt;li>C++, Flutter，KMP、Python、Shell都会一点，Flutter略会&lt;/li>
&lt;li>写过鸿蒙，也写过小程序&lt;/li>
&lt;li>看好跨平台和AI&lt;/li>
&lt;li>用AI写了很多小工具&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="爱好">
 爱好&lt;span class="heading__anchor"> &lt;a href="#%e7%88%b1%e5%a5%bd">#&lt;/a>&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>看书&lt;/li>
&lt;li>喜欢旅行，喜欢感受不一样的人文美食&lt;/li>
&lt;/ul>
&lt;h2 class="heading" id="社交媒体">
 社交媒体&lt;span class="heading__anchor"> &lt;a href="#%e7%a4%be%e4%ba%a4%e5%aa%92%e4%bd%93">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>Github：&lt;a href="https://github.com/opoojkk">opoojkk&lt;/a>&lt;/p></description></item><item><title>Gradle显示任务面板技巧</title><link>https://opoojkk.com/posts/2023/10/05/gradle%E6%98%BE%E7%A4%BA%E4%BB%BB%E5%8A%A1%E9%9D%A2%E6%9D%BF%E6%8A%80%E5%B7%A7/</link><pubDate>Thu, 05 Oct 2023 10:11:23 +0800</pubDate><guid>https://opoojkk.com/posts/2023/10/05/gradle%E6%98%BE%E7%A4%BA%E4%BB%BB%E5%8A%A1%E9%9D%A2%E6%9D%BF%E6%8A%80%E5%B7%A7/</guid><description>&lt;h1 class="heading" id="android-studio-gradle-tasks">
 Android Studio Gradle Tasks&lt;span class="heading__anchor"> &lt;a href="#android-studio-gradle-tasks">#&lt;/a>&lt;/span>
&lt;/h1>&lt;h2 class="heading" id="giraffe之前版本">
 Giraffe之前版本&lt;span class="heading__anchor"> &lt;a href="#giraffe%e4%b9%8b%e5%89%8d%e7%89%88%e6%9c%ac">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>设置路径：File -&amp;gt; Settings -&amp;gt; Experimental
取消勾选“Do not build Gradle task list during Gradle sync”后，点击“Sync Project with Gradle Files”&lt;/p>
&lt;p>&lt;img src="https://i.imgur.com/I5GxPZ2.jpg">
&lt;img src="https://i.imgur.com/e2tys1J.jpg">&lt;/p>
&lt;h2 class="heading" id="giraffe202231">
 Giraffe(2022.3.1)&lt;span class="heading__anchor"> &lt;a href="#giraffe202231">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>设置路径同上
&lt;img src="https://i.imgur.com/dttxqeB.jpg">
不要点击“Layout Inspector”!&lt;/p>
&lt;p>勾选后点击“点击Sync Project with Gradle Files”&lt;/p></description></item><item><title>RecyclerView.shouldIgnore() NPE问题排查日记</title><link>https://opoojkk.com/posts/2023/04/08/recyclerview.shouldignore-npe%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5%E6%97%A5%E8%AE%B0/</link><pubDate>Sat, 08 Apr 2023 19:59:12 +0800</pubDate><guid>https://opoojkk.com/posts/2023/04/08/recyclerview.shouldignore-npe%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5%E6%97%A5%E8%AE%B0/</guid><description>&lt;h2 class="heading" id="背景">
 背景&lt;span class="heading__anchor"> &lt;a href="#%e8%83%8c%e6%99%af">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>这周在写商业化需求(广告)时，为了方便复用，简单改了下传入的Bean。第一次运行时没有一切正常，第二次之后再运行时，提示空指针异常，异常的堆栈是RecyclerView，异常信息(当时没有保存日志，找了其他人相同异常的堆栈，原因是同一个)：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">java&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lang&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">NullPointerException&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Attempt&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">virtual&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">method&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">&amp;#39;&lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recyclerview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">RecyclerView$ViewHolder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">shouldIgnore&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">on&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">object&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">reference&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recyclerview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">dispatchLayoutStep3&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">3951&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recyclerview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">dispatchLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">3652&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recyclerview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">4194&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">19590&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6053&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">appcompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayoutCompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setChildFrame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayoutCompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1645&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">appcompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayoutCompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layoutVertical&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayoutCompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1499&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">appcompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayoutCompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayoutCompat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1407&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">19590&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6053&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recyclerview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">RecyclerView$LayoutManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layoutDecoratedWithMargins&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">9322&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recyclerview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayoutManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layoutChunk&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayoutManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1615&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recyclerview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayoutManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">fill&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayoutManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1517&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recyclerview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayoutManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayoutChildren&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayoutManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">612&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recyclerview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">dispatchLayoutStep2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">3924&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recyclerview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">dispatchLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">3641&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">recyclerview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">4194&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">19590&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6053&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">constraintlayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ConstraintLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ConstraintLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1915&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">19590&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6053&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setChildFrame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1791&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layoutVertical&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1635&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1544&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">19590&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6053&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layoutChildren&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">323&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">261&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">19590&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6053&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setChildFrame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1791&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layoutVertical&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1635&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1544&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">19590&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6053&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layoutChildren&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">323&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">261&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">19590&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6053&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setChildFrame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1791&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layoutVertical&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1635&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LinearLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1544&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">19590&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6053&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layoutChildren&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">323&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">widget&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FrameLayout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">261&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">internal&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">policy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">DecorView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">onLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DecorView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">758&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">19590&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6053&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewRootImpl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">performLayout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewRootImpl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">2484&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewRootImpl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">performTraversals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewRootImpl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">2200&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewRootImpl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">doTraversal&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewRootImpl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">1386&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ViewRootImpl$TraversalRunnable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewRootImpl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6733&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Choreographer$CallbackRecord&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Choreographer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">911&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Choreographer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">doCallbacks&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Choreographer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">723&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">2018&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">10&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">20&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">19&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">24&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">30&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">408&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">20730&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">20730&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">wynk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">basic&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">E&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">AndroidRuntime&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Choreographer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">doFrame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Choreographer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">658&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">view&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Choreographer$FrameDisplayEventReceiver&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Choreographer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">897&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Handler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">handleCallback&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Handler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">789&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Handler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">dispatchMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Handler&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">98&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Looper&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">loop&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Looper&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">164&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ActivityThread&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ActivityThread&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">6541&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lang&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">reflect&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Method&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Native&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Method&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">internal&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">Zygote$MethodAndArgsCaller&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Zygote&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">240&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">internal&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ZygoteInit&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ZygoteInit&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">java&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">767&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>下意识以为又是Android Studio抽风(公司电脑经常的事)，清除缓存+重启+重装一套组合拳下来问题还是没有解决。终于意识到就是代码有问题。
改动影响的范围很小，很容易锁定。异常对战中出现RecyclerView，而且之前没有出现过，怀疑是写法有问题。看写法和之前又没有什么差别，开始迷茫怀疑人生&amp;hellip;.&lt;/p>
&lt;h2 class="heading" id="找问题">
 找问题&lt;span class="heading__anchor"> &lt;a href="#%e6%89%be%e9%97%ae%e9%a2%98">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>看不出来，找到了抛出异常的代码&lt;code>ViewHolder.shouldIgnore()&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 只保留了关键的两行代码&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">ViewHolder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">holder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getChildViewHolderInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mChildHelper&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getChildAt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">holder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">shouldIgnore&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// getChildViewHolderInt&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewHolder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getChildViewHolderInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">View&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">child&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">child&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">LayoutParams&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">child&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getLayoutParams&lt;/span>&lt;span class="p">()).&lt;/span>&lt;span class="na">mViewHolder&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>其实到这问题已经差不多了，child为null或者LayoutParams中的mViewHolder为null才会有这种情况，代码中又没有移除子View的操作，只可能mViewHolder为null，但&amp;hellip;没看出来。
等到把异常信息&lt;code>RecyclerView$ViewHolder.shouldIgnore() NullPointerException&lt;/code>甩到Google上找到了&lt;a href="https://github.com/airbnb/epoxy/issues/585">这&lt;/a>
回项目里边找代码，果然，代码里为了重新计算View的宽高，把ViewHolder#mItemView的LayoutParams用一个新建的对象覆盖了，类似这样：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">mBinding&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getRoot&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">setLayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RecyclerView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LayoutParams&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LayoutParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MATCH_PARENT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LayoutParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">WRAP_CONTENT&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>LayoutParams里的mViewHolder肯定也就是null了。&lt;/p>
&lt;h2 class="heading" id="解决">
 解决&lt;span class="heading__anchor"> &lt;a href="#%e8%a7%a3%e5%86%b3">#&lt;/a>&lt;/span>
&lt;/h2>&lt;p>原因就是替换了原来的LayoutParams，而新的LayoutParams中mViewHolder又没有赋值。用mAdBinding.getRoot().getLayoutParams()再改属性就可以了。像这样：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LayoutParams&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">layoutParams&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mBinding&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getRoot&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getLayoutParams&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">layoutParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">height&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LayoutParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">WRAP_CONTENT&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">layoutParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">width&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ViewGroup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">LayoutParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">MATCH_PARENT&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>归档</title><link>https://opoojkk.com/archives/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://opoojkk.com/archives/</guid><description/></item></channel></rss>