<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Xiaoming‘s BLOG</title>
  
  <subtitle>个人博客</subtitle>
  <link href="https://adewu.github.io/atom.xml" rel="self"/>
  
  <link href="https://adewu.github.io/"/>
  <updated>2024-02-13T08:48:37.697Z</updated>
  <id>https://adewu.github.io/</id>
  
  <author>
    <name>晓明</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>跨平台UI渲染引擎方案对比</title>
    <link href="https://adewu.github.io/2019/05/02/cross_platform_pk/"/>
    <id>https://adewu.github.io/2019/05/02/cross_platform_pk/</id>
    <published>2019-05-02T07:21:00.000Z</published>
    <updated>2024-02-13T08:48:37.697Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1、第一类-WebView-跨平台技术"><a href="#1、第一类-WebView-跨平台技术" class="headerlink" title="1、第一类 WebView 跨平台技术"></a>1、第一类 WebView 跨平台技术</h3><p>第一代跨平台技术主要以 Webview 容器，代表有 PhoneGap&#x2F;Cordova 。优点：功能丰富，标准强，历史悠久，有强大的前端生态支持；是目前最成功的跨平台渲染容器。支付宝及微信更是以此为载体，打造小程序内核。第一代渲染引擎主要缺点在于性能方面及高级组件方面，流畅性始终与 Native 无法媲美。</p><p>为何会这样，我们以 Blink 为例从三个方面来看此原因。</p><h4 id="1-1-WebView-的基础架构及线程模型"><a href="#1-1-WebView-的基础架构及线程模型" class="headerlink" title="1.1 WebView 的基础架构及线程模型"></a>1.1 WebView 的基础架构及线程模型</h4><p><img src="/../imgs/blink_arch.png" alt="alt text" title="Blink基础架构示意图"></p><p>Android 平台 WebView 采用多进程架构，主要分为 Browser 进程， Render 进程和 GPU 进程。 Browser 进程负责用户输入， Touch 事件处理、平台相关的对接等功能。 Render 进程 Main Thread 负责 JS 的执行， CSS 解析， Layout Paint ，输出 DisplayList 供 CC 使用。 Work Threads 进行图片的编解码。 Compositor Threads 负责 Layer 的合成，和 Tile 分片；将分片输出成 Bitmap 或者 GL 指令，通过 IPC 输出到 GPU Process 。 GPU Process 的 GPUThread 线程负具体指令的绘制，将绘制指令渲染输出到显示器上。</p><h4 id="1-2-WebView渲染流程及线程模型"><a href="#1-2-WebView渲染流程及线程模型" class="headerlink" title="1.2 WebView渲染流程及线程模型"></a>1.2 WebView渲染流程及线程模型</h4><p><img src="/../imgs/html_reander_process.png" alt="alt text" title="一个像素从更新发起到显示的具体流程"></p><p>下面是 WebView 渲染具体的具体执行的线程模型：</p><p><img src="/../imgs/webview_threads_model.png" alt="alt text"></p><p>WebView 的 JS 执行， DOM 构建， RenderObject 的构建， Layout Paint 都在主线程执行。 Compositor Thread 负责 Layer 合成， Worker Thread 进行图片解码及 GPU 光栅化。 GPU Thread 进行最终的指令合成和渲染显示。</p><p><img src="/../imgs/webview_gpu_process.png" alt="alt text"></p><p>上图 Render 进程是与 GPU 进程 GPU Thread 的交互图，WebView 每一帧的更新都需 IPC 调用更新到 GPU Process ，这种 IPC 模型相对线程通信还是比较昂贵的。</p><h4 id="1-3-HTML5-作为开放的技术标准，历史悠久，包袱多"><a href="#1-3-HTML5-作为开放的技术标准，历史悠久，包袱多" class="headerlink" title="1.3 HTML5 作为开放的技术标准，历史悠久，包袱多"></a>1.3 HTML5 作为开放的技术标准，历史悠久，包袱多</h4><p>HTML5 标准在 Android&#x2F;iOS 引擎实现不统一， Android 平台 Chrome(blink) ， iOS 平台 WKWebview(Webkit) 。标准的实现难度非常高，每个引擎代码行数都在 500-1000 万行；庞大的代码规模导致入门和改动成本比较高，引擎定制成本非常复杂。目前从国内来看， UC &#x2F;阿里云有能力做内核级别的高级定制开发，其它团队难以进行大规模内核级别的高级定制。无线端 Native 平台成熟的 List Scroller Cell 等高性能组件，无法在 WebView 内核级别做有效的支持。以小程序内嵌 NativeView 所需的同层渲染技术为例，在两个平台要做不同的技术实现。 HTML 规范从提出到落地时间非常长，一般 3-5 年后才能普及，业务难以等待。</p><h4 id="1-4-WebView-渲染引擎设计的上的缺陷"><a href="#1-4-WebView-渲染引擎设计的上的缺陷" class="headerlink" title="1.4 WebView 渲染引擎设计的上的缺陷"></a>1.4 WebView 渲染引擎设计的上的缺陷</h4><ul><li>JS Execute，Layout， Paint 都在MainThread ，无法并行化。</li><li>JS 的性能赶不上 Native Tookit 的 Java Dart Object-C 等编译型语言，执行复杂逻辑时会卡顿。</li><li>渲染流水线非常长，导致浏览器对合成器动画和非合成器动画区分对待，非合成器动画性能不佳。</li><li>OpenGL 设计上是推荐单线程模型，一个 Context 同时只能运行一个线程使用。 GPU Thread 运行在单独 GPU 进程， Render 进程无法访问 GPU 进程的 - OpenGL Context ，两个进程无法 Texture 共享资源。 Render 进程只能输出 Bitmap&#x2F;Command Buffer 通过 IPC 传递给 GPU 进程，无法直接在 GPU 进程的 Open GL Context 做直接光栅化，难以充分发挥现代 GPU 的性能。</li><li>光栅化是异步进行的，进行惯性滚动时，会出现白屏，这个是 Webview 始终无法避免的问题。</li><li>设备平台众多，需要兼容CPU渲染，无法进行 All In GPU 的设计。</li></ul><h3 id="2、第二类-Weex-React-Native-跨平台技术"><a href="#2、第二类-Weex-React-Native-跨平台技术" class="headerlink" title="2、第二类 Weex&#x2F;React-Native 跨平台技术"></a>2、第二类 Weex&#x2F;React-Native 跨平台技术</h3><p>第二类跨平台框架主要以 Weex&#x2F;React-Native&#x2F; 鸟巢等为代表，这种技术最大化的复用前端的生态和 Native 的生态体系，把 Native View 的高性能组件积累输出给前端的技术体系。此方案和浏览器的最大区别在于 Script 的执行和 Native View 渲染体系。</p><h4 id="2-1-Weex-的基础架构"><a href="#2-1-Weex-的基础架构" class="headerlink" title="2.1 Weex 的基础架构"></a>2.1 Weex 的基础架构</h4><p><img src="/../imgs/weex_arch.png" alt="alt text"></p><p>Weex 对外通过 Rax 和 Vue 前端框架进行功能输出，前端框架下有一层 JS Framework 来实现 dom 的功能。 WeexCore 负责基础的 Flex Layout ，然后通过 Component 分别对接到 Android&#x2F;iOS 的 Platform Native View 体系。</p><h4 id="2-2-Weex-基础架构-JS-执行上相对-WebView-的优势"><a href="#2-2-Weex-基础架构-JS-执行上相对-WebView-的优势" class="headerlink" title="2.2 Weex 基础架构 JS 执行上相对 WebView 的优势"></a>2.2 Weex 基础架构 JS 执行上相对 WebView 的优势</h4><p><img src="/../imgs/weex_webview_thread.png" alt="alt text"></p><p>Weex 体系中， JS 的执行在 JS Thread，Layout 执行在独立的 Layout Thread ，渲染执行在系统的 MainThread ，三个线程相互独立，并行执行。在 WebView 的体系中 JS 的执行、 Layout 、 Paint 都在 MainThread ，相互影响，在进行复杂任务时会导致界面卡顿。</p><h4 id="2-3-Weex-体系渲染流水线及-OpenGL-设计上和-WebView-区别"><a href="#2-3-Weex-体系渲染流水线及-OpenGL-设计上和-WebView-区别" class="headerlink" title="2.3 Weex 体系渲染流水线及 OpenGL 设计上和 WebView 区别"></a>2.3 Weex 体系渲染流水线及 OpenGL 设计上和 WebView 区别</h4><p>Android Native 线程模型和 WebView 线程模型对比如下图：</p><p><img src="/../imgs/weex_webview_render.png" alt="alt text"></p><p><img src="/../imgs/weex_webview_opengl.png" alt="alt text"></p><p>两图对比可以看出：</p><ul><li>Android Native 采用更轻量级的渲染流水线，能更快更高效的的响应事件。</li><li>RenderThread 直接操作 OpenGLContext ，进行 Direct GPU Raster ，充分发挥现代 GPU 的特性，提供高性能渲染和流畅的体验。</li><li>部分耗时操作，如 Bitmap 上传 Texture ， TextureThread 上传到 Share Open GL Context 中， Texture 完成后通知主线程进行绘制，通过 Share Open GL Context 与主线程共享 Texture 等资源。 WebView 只能在 Render Process 内部进行 Texture 的共享， RenderProcess 无法与 GPU Process 共享 Texture 等资源。</li><li>Android Native 有 RecycleView ViewPager 等高级组件，每个高级组件都做了性能的最佳实践；浏览器上的高级组件只能通过 JS 模拟实现，优化定制效率低。</li><li>浏览器流水线设计复杂，需要考虑到 PC 、手机、嵌入式设备等多种复杂的环境，不少设备上木有 GPU ，只能进行 CPU 渲染。无法像 Android Native 体系一样进行 All In GPU 的体系设计，全面发挥现代 GPU 的性能。</li></ul><h4 id="2-4-Weex-体系在跨平台及性能上的不足"><a href="#2-4-Weex-体系在跨平台及性能上的不足" class="headerlink" title="2.4 Weex 体系在跨平台及性能上的不足"></a>2.4 Weex 体系在跨平台及性能上的不足</h4><p>Weex 体系充分将 Native 的 View 体系输出到前端体系中，在进行 Android&#x2F;iOS Native View 的封装过程中，存在不少难以逾越的障碍。如：难以磨平的双端一致性问题、复杂样式能力难以实现、 Layout 动画需要执行两次（WeexCore Layout 和 Android Native 本身的 Layout ）。组件的封装成本随着复杂度增加也越来越高，难以逾越 Native View 限制提供更细致的 W3C 标准能力。</p><h4 id="2-5-React-Native与Weex的比较"><a href="#2-5-React-Native与Weex的比较" class="headerlink" title="2.5 React-Native与Weex的比较"></a>2.5 React-Native与Weex的比较</h4><p>相同点：</p><ol><li>有自己的手脚架工具，开发迅速，开发支持HotReloading</li><li>解析脚本到渲染都需要原生代码中加载对应支持库的framework，渲染的时候是通过解析库将对应的脚本解析成原生代码，因此解析都会消耗一定的性能</li><li>布局方式与web布局相似，采用的是Flexbox</li><li>与原生交互方式都是通过宏或注解的形式注入方法</li><li>通过解析虚拟dom的形式，通过各自的平台对应的解析库来进行转换</li><li>部署的时候都会输出bundle</li></ol><p>不同点：</p><ol><li>react-native与react相似,weex则是vue的体系</li><li>设计角度上react-native与很多oop语言类似，weex则更像web开发的html，react-native没有像weex一样将 页面(templete)，功能(script)，样式(css)在一个.vue中分成三个特定的代码编写位置，react-native可以在页面中定义执行脚本</li><li>加载包：react-native 加载包会通过一个入口文件加载整个js.bundle，weex则可以分别加载对应的js，这可能会对应的加载速度造成影响</li><li>输出包的形式：react-native输出的是一个大的bundle文件，weex输出的是多个.js文件的形式，react-native输出包的时候会吧依赖的framework输出到bundle中,而weex则是输出具体的功能逻辑包，相应的依赖包则由weex的framework层来提供。weex能够天然的支持分包问题解决了更新迭代的加载速度问题，react-native则需要通过bundle-metro或者其他形式来进行分包，以优化包的加载速度</li><li>react-native有很多设计架构来支持，weex貌似是mvc，react-native的状态管理更好实现?</li><li>底层类库及功能库的支持熵react-native更加的丰富</li><li>weex能够编译出web端的代码,react-native不能，react-native更专注mobile</li><li>配置层面react-native只提供一个package.json来组织项目清晰明了，weex则做了很多的环境配置</li><li>打包方式上的差异,react-native打包通过命令打jsbundle,然后在通过原生命令打成对应平台的安装包,weex打包的话可以直接打成对应平台的安装包，如果只是打增量的js包的话则是通过weex run的形式来输出对应的包</li></ol><h3 id="3、第三类-Flutter-跨平台技术"><a href="#3、第三类-Flutter-跨平台技术" class="headerlink" title="3、第三类 Flutter 跨平台技术"></a>3、第三类 Flutter 跨平台技术</h3><p>2018 年 Flutter 横空出世，通过 Dart 语言构建一套跨平台的开发组件，所有组件基于 Skia 引擎自绘，在性能上和 Native 平台的 View 相媲美。引起大家广泛关注，充分验证了通过绘制构建组件做到 Native View 媲美的 UI 渲染引擎的可行性。</p><h4 id="3-1-Flutter-的基础架构"><a href="#3-1-Flutter-的基础架构" class="headerlink" title="3.1 Flutter 的基础架构"></a>3.1 Flutter 的基础架构</h4><p><img src="/../imgs/flutter_arch.png" alt="alt text" title="Flutter基础架构"></p><p>Flutter 基础架构主要分为三个部分：</p><ul><li>Framework 层：包含 Animation Painting Gestures RenderIng Widgets 等模块；提供基础的 UI 组件。</li><li>Engine 层包括： Dart VM Manager ， Frame Pipeline Rendering , TextLayout 等模块，主要负责 Skia 的渲染调度以及 Layer Tree 等合成。</li><li>Embedber 层分别对接 Android&#x2F;iOS 平台层，进行事件对接， Surface 对接，以及 Native 平台接口调用的插件机制。</li></ul><h4 id="3-2-Flutter-的渲染流水线和-Android-Native-对比"><a href="#3-2-Flutter-的渲染流水线和-Android-Native-对比" class="headerlink" title="3.2 Flutter 的渲染流水线和 Android Native 对比"></a>3.2 Flutter 的渲染流水线和 Android Native 对比</h4><h5 id="3-2-1-Flutter-的渲染流水线"><a href="#3-2-1-Flutter-的渲染流水线" class="headerlink" title="3.2.1 Flutter 的渲染流水线"></a>3.2.1 Flutter 的渲染流水线</h5><p><img src="/../imgs/flutter_render_pipline.png" alt="alt text" title="Flutter 渲染流水线"></p><p>Embedber 层收到 VSync 信号，把此信号传递给 Dart VM 中运行 Flutter Framework 。 Dart UI FrameWork 首先处理 Animation 差值，然后更新 Widget Tree ，接着更新 Element Tree ，最后更新 RenderObject Tree ，发起 Paint 流程。再由 SceneBuilder 输出 Layer Tree ，提交到 GPU 线程进行这一帧的阻塞式合成，合成完成后开始下一帧。</p><h5 id="3-2-2-Android-Native-渲染流水线"><a href="#3-2-2-Android-Native-渲染流水线" class="headerlink" title="3.2.2 Android Native 渲染流水线"></a>3.2.2 Android Native 渲染流水线</h5><p><img src="/../imgs/android_render_pipline.png" alt="alt text" title="Android Native渲染流水线"></p><p>Android 系统 Native View Framework 收到 VSync 信号后，首先进行 Touch、Input 等事件处理，再进行 Animation 的更新处理，之后 View Tree 发起 Measure和 Layout 完成布局过程。通过 Draw 把本次更新的脏节点更新的 DispayList 绘制指令同步到 RenderThread 。 RenderThread 通过 DisplayList 对 RenderNode 更新合成，把指令转换成 OpenGL 绘制指令输出到 GPU ， 整个流程和Flutter基本相同。</p><h4 id="3-3-Flutter-和-Android-在渲染方面相似点和不同点"><a href="#3-3-Flutter-和-Android-在渲染方面相似点和不同点" class="headerlink" title="3.3 Flutter 和 Android 在渲染方面相似点和不同点"></a>3.3 Flutter 和 Android 在渲染方面相似点和不同点</h4><p>a、Flutter 和Android 共同点：</p><p>采用精简的渲染流水线，从事件到 GPU 更新整体渲染流程很相似</p><p>在 GPU 层面进行直接光栅化，充分利用现代 GPU 的高性能渲染性能。</p><p>都采用 OpenGL Share Context 进行设计，异步进行图片 Texture 上传，共享图片等 Texture 资源。</p><p>最新版本 Android Native 和 Flutter 底层共同采用 Skia 引擎进行合成绘制。</p><p>b、Flutter 和 Android 不同点：</p><p>Android Native 采用 Java 构建 UIFramework ， Flutter 采用 Dart 。</p><p>Android Native 支持局部更新，在 Open GL 层面做了非常多的深层次的优化， Flutter 这块工作目前不足。</p><p>Android HW UI 是系统应用，可根据手机机型及 GPU 进行参数调优，深度定制；这是 Flutter 框架做不到的。</p><p>目前 Android 生态 UI 库比较全面，模块之间融合成本低。 Flutter 自成体系，和 Native View 融合上存在一定成本。</p><h4 id="3-4-Flutter-相对-Weex-Native-的优势与不足"><a href="#3-4-Flutter-相对-Weex-Native-的优势与不足" class="headerlink" title="3.4 Flutter 相对 Weex Native 的优势与不足"></a>3.4 Flutter 相对 Weex Native 的优势与不足</h4><p>Flutter 引擎基于 Skia 构建跨平台组件，解决了 Weex 难以解决的双端一致性等问题。上层采用 Dart 语言，没有利用到前端最强大的 JavaScript 生态；和 NativeView 的融合上也存在一些问题，难以复用 Native 多年来积累的强大组件。这些是它相对于 Weex 的不足。在性能方面， Flutter 和 Weex 解决方案，本质上基本相同，实际页面性能取决于最佳实践，目前实际情况看 Weex 的 NativeView 性能优势更强一些。</p><h3 id="4、前三类跨平台技术的总结"><a href="#4、前三类跨平台技术的总结" class="headerlink" title="4、前三类跨平台技术的总结"></a>4、前三类跨平台技术的总结</h3><p>上面整体介绍了前三类的跨平台渲染引擎，下面从特点和技术方面来总结上面的跨平台渲染引擎。</p><p><img src="/../imgs/cross_platform_compare.png" alt="alt text" title="Android Native渲染流水线"></p><p>备注： WebView性能目前随着手机性能的提升而逐步提升，在某些高端机上简单页面的性能已经非常流畅，但在复杂页面尤其是有交互的页面和Weex还是有一定的性能差距。</p><h4 id="4-2-跨平台渲染引擎的技术上进行对比"><a href="#4-2-跨平台渲染引擎的技术上进行对比" class="headerlink" title="4.2 跨平台渲染引擎的技术上进行对比"></a>4.2 跨平台渲染引擎的技术上进行对比</h4><p><img src="/../imgs/cross_platform_tech_conpare.png" alt="alt text" title="Android Native渲染流水线"></p><p>参考：</p><p><a href="https://developer.aliyun.com/article/710474?utm_content=g_1000069134#slide-18">大比拼 | 下一代高性能跨平台UI渲染引擎</a></p><p><a href="https://www.cnblogs.com/codetime/p/10417038.html">React-Native与Weex的比较</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;1、第一类-WebView-跨平台技术&quot;&gt;&lt;a href=&quot;#1、第一类-WebView-跨平台技术&quot; class=&quot;headerlink&quot; title=&quot;1、第一类 WebView 跨平台技术&quot;&gt;&lt;/a&gt;1、第一类 WebView 跨平台技术&lt;/h3&gt;&lt;p&gt;第一</summary>
      
    
    
    
    <category term="Flutter" scheme="https://adewu.github.io/categories/Flutter/"/>
    
    
    <category term="Flutter" scheme="https://adewu.github.io/tags/Flutter/"/>
    
  </entry>
  
  <entry>
    <title>Jetpack架构组件：LiveData</title>
    <link href="https://adewu.github.io/2019/04/27/jetpack_livedata/"/>
    <id>https://adewu.github.io/2019/04/27/jetpack_livedata/</id>
    <published>2019-04-27T04:21:00.000Z</published>
    <updated>2024-02-13T04:36:35.290Z</updated>
    
    <content type="html"><![CDATA[<h3 id="LiveData的定义"><a href="#LiveData的定义" class="headerlink" title="LiveData的定义"></a>LiveData的定义</h3><p>LiveData是一个可观察数据的持有者, 但是不像普通的可观察数据, LiveData绑定了生命周期，例如Activity, Fragment的生命周期。有点抽象, 还是先了解一下关键的类。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Observer是一个接口，观察者。当数据改变时就回调onChanged</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Observer</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">onChanged</span><span class="params">(<span class="meta">@Nullable</span> T t)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//LiveData类是个抽象类，这里先看看有什么暴露出去的方法</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">LiveData</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="comment">//添加观察者观察, 传入LifecycleOwner，用于来绑定生命周期</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">observe</span><span class="params">(LifecycleOwner owner, Observer&lt;T&gt; observer)</span></span><br><span class="line">    <span class="comment">//添加观察者观察, 但是是没有LifecycleOwner</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">observeForever</span><span class="params">(Observer&lt;T&gt; observer)</span> </span><br><span class="line">    <span class="comment">//移除观察者   </span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeObserver</span><span class="params">(Observer&lt;T&gt; observer)</span> </span><br><span class="line">    <span class="comment">//移除某个LifecycleOwner里所有的观察者    </span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeObservers</span><span class="params">(<span class="keyword">final</span> LifecycleOwner owner)</span></span><br><span class="line">    <span class="comment">//是否有观察者    </span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasObservers</span><span class="params">()</span>    </span><br><span class="line">    <span class="comment">//是否有活的观察者    </span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasActiveObservers</span><span class="params">()</span></span><br><span class="line">&#125;    </span><br><span class="line"><span class="comment">//MutableLiveData继承LiveData, 多开放2个方法。</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MutableLiveData</span>&lt;T&gt; <span class="keyword">extends</span> <span class="title class_">LiveData</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">postValue</span><span class="params">(T value)</span> <span class="comment">//post一个数据 </span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setValue</span><span class="params">(T value)</span>  <span class="comment">//设置一个数据</span></span><br><span class="line">    <span class="comment">//postValue和setValue的区别是：post可以在子线程用，而setValue只能在UI线程调用</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>介绍完了主要的类，下面举个例子：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这段代码通常在Activity或者Fragment中, 用liveData去添加一个观察者</span></span><br><span class="line"><span class="comment">// AppCompatActivity或者Fragment已经LifecycleOwner，通常传this即可</span></span><br><span class="line"><span class="comment">// 当User数据有变化时，onChanged方法会被调用, 用于更新UI</span></span><br><span class="line">liveData.observe(<span class="built_in">this</span>, <span class="keyword">new</span> <span class="title class_">Observer</span>&lt;User&gt;() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onChanged</span><span class="params">(<span class="meta">@Nullable</span> User user)</span> &#123;</span><br><span class="line">    <span class="comment">//更新UI</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//下面这段代码通常在ViewModel中，这里liveData和上面代码的liveData是同一个</span></span><br><span class="line"><span class="comment">//可以通过postValue或者setValue去改成user数据, 有数据改变时, onChanged方法会被回调</span></span><br><span class="line"><span class="comment">//假如一个场景, 在ViewModel中，通过网络请求去拿user数据，然后postValue就会去通知UI层onChanged去更新UI</span></span><br><span class="line"><span class="comment">//liveData大概就是这个流程</span></span><br><span class="line">liveData = <span class="keyword">new</span> <span class="title class_">MutableLiveData</span>&lt;User&gt;()</span><br><span class="line">liveData.postValue(user)<span class="comment">// 或者setValue(user)</span></span><br></pre></td></tr></table></figure><p>下面还是看看LiveData源码去了解一下原理</p><h3 id="工作原理"><a href="#工作原理" class="headerlink" title="工作原理"></a>工作原理</h3><p>这里以observe(LifecycleOwner owner, Observer observer) 和 postValue之间数据的通讯为例分析。下面看LiveData.observe源码:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">@MainThread</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">observe</span><span class="params">(<span class="meta">@NonNull</span> LifecycleOwner owner, <span class="meta">@NonNull</span> Observer&lt;T&gt; observer)</span> &#123;</span><br><span class="line">       <span class="comment">//判断当前生命周期是DESTROYED, 立马return, 都销毁了，添加观察者没意义</span></span><br><span class="line">       <span class="keyword">if</span> (owner.getLifecycle().getCurrentState() == DESTROYED) &#123;</span><br><span class="line">           <span class="comment">// ignore</span></span><br><span class="line">           <span class="keyword">return</span>;</span><br><span class="line">       &#125;</span><br><span class="line">       </span><br><span class="line">       </span><br><span class="line">       <span class="type">LifecycleBoundObserver</span> <span class="variable">wrapper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LifecycleBoundObserver</span>(owner, observer);</span><br><span class="line">       <span class="type">ObserverWrapper</span> <span class="variable">existing</span> <span class="operator">=</span> mObservers.putIfAbsent(observer, wrapper);</span><br><span class="line">       <span class="comment">//LifecycleBoundObserver继承ObserverWrapper, LifecycleBoundObserver实现LifecycleOwner</span></span><br><span class="line">       <span class="comment">//mObservers这是一个Map, key是observer, value是ObserverWrapper</span></span><br><span class="line">       <span class="keyword">if</span> (existing != <span class="literal">null</span> &amp;&amp; !existing.isAttachedTo(owner)) &#123;</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Cannot add the same observer&quot;</span></span><br><span class="line">                   + <span class="string">&quot; with different lifecycles&quot;</span>);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">if</span> (existing != <span class="literal">null</span>) &#123;</span><br><span class="line">           <span class="keyword">return</span>;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="comment">//添加生命周期的观察者</span></span><br><span class="line">       owner.getLifecycle().addObserver(wrapper);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>接着看LifecycleBoundObserver和ObserverWrapper片段:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">LifecycleBoundObserver</span> <span class="keyword">extends</span> <span class="title class_">ObserverWrapper</span> <span class="keyword">implements</span> <span class="title class_">GenericLifecycleObserver</span> &#123;</span><br><span class="line">        <span class="meta">@NonNull</span> <span class="keyword">final</span> LifecycleOwner mOwner;</span><br><span class="line">        </span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="type">boolean</span> <span class="title function_">shouldBeActive</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="comment">//至少是STARTED状态</span></span><br><span class="line">            <span class="keyword">return</span> mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//GenericLifecycleObserver继承自LifecycleObserver, 当生命周期改变时onStateChanged会被回调</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onStateChanged</span><span class="params">(LifecycleOwner source, Lifecycle.Event event)</span> &#123;</span><br><span class="line">            <span class="comment">//生命周期处于DESTROYED时, 移除观察者，mObserver是livedata.observe时传进来</span></span><br><span class="line">            <span class="keyword">if</span> (mOwner.getLifecycle().getCurrentState() == DESTROYED) &#123;</span><br><span class="line">                removeObserver(mObserver);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//shouldBeActive()方法实现在当前类， 调用isAtLeast(STARTED), 至少是STARTED状态才是true</span></span><br><span class="line">            <span class="comment">//就是生命周期在STARTED和RESUME状态时, 才是true</span></span><br><span class="line">            <span class="comment">//接着调用了父类的activeStateChanged</span></span><br><span class="line">            activeStateChanged(shouldBeActive());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">ObserverWrapper</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">void</span> <span class="title function_">activeStateChanged</span><span class="params">(<span class="type">boolean</span> newActive)</span> &#123;</span><br><span class="line">            <span class="comment">// mActive状态相同立即返回</span></span><br><span class="line">            <span class="keyword">if</span> (newActive == mActive) &#123;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// immediately set active state, so we&#x27;d never dispatch anything to inactive</span></span><br><span class="line">            <span class="comment">// owner</span></span><br><span class="line">            mActive = newActive;</span><br><span class="line">            <span class="comment">//就是生命周期在STARTED和RESUME时, mActive为true, 其他为false</span></span><br><span class="line">            <span class="type">boolean</span> <span class="variable">wasInactive</span> <span class="operator">=</span> LiveData.<span class="built_in">this</span>.mActiveCount == <span class="number">0</span>;</span><br><span class="line">            LiveData.<span class="built_in">this</span>.mActiveCount += mActive ? <span class="number">1</span> : -<span class="number">1</span>;</span><br><span class="line">            <span class="comment">//当mActive=true时， mActiveCount会加1, 当mActive=false时， mActiveCount会减1。</span></span><br><span class="line">            <span class="comment">//这里为什么要加1和减1呢，结合Activity生命周期去考虑，当Activity.onCreate -&gt; Activity.onStart -&gt; Activity.onResume, State由CREATED-&gt;STARTED-&gt;RESUMED,这个过程当到STARTED时mActive为true,mActiveCount加1后为1, 接着RESUMED, mActive还是true, 这方法第一句（newActive == mActive）条件成立直接return, mActiveCount此时还是1。</span></span><br><span class="line">            <span class="comment">//当Activity.onResume-&gt;Activity.onPause-&gt;Activity.onStop，State由RESUMED-&gt;STARTED-&gt;CREATED, Activity.onStop后State为CREATED, isAtLeast(STARTED)就为false, 此时mActive=false, mActiveCount减1，mActiveCount为0啦。</span></span><br><span class="line">            <span class="comment">//当Activity.onStop-&gt;Activity.onStart-&gt;Activity.onResume, State由CREATED-&gt;STARTED-&gt;RESUMED, mActive为true,mActiveCount加1后为1, mActiveCount此时为1。</span></span><br><span class="line">            <span class="keyword">if</span> (wasInactive &amp;&amp; mActive) &#123;</span><br><span class="line">                <span class="comment">//根据上面一大段分析后，onActive会在Activity首次显示UI调用，从后台返回或者从另一个Activity回退后又调用一次。(打开透明Activity不走onStop,这种情况除外)</span></span><br><span class="line">                onActive();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (LiveData.<span class="built_in">this</span>.mActiveCount == <span class="number">0</span> &amp;&amp; !mActive) &#123;</span><br><span class="line">                <span class="comment">// Activity.onStop后调用</span></span><br><span class="line">                onInactive();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (mActive) &#123;</span><br><span class="line">                <span class="comment">//分发数据, 跟踪dispatchingValue方法，会到LiveData.considerNotify方法</span></span><br><span class="line">                dispatchingValue(<span class="built_in">this</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">considerNotify</span><span class="params">(ObserverWrapper observer)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!observer.mActive) &#123; </span><br><span class="line">            <span class="comment">//mActive可以知道是否处于onStop状态, 是Stop状态就不要分发数据</span></span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!observer.shouldBeActive()) &#123;</span><br><span class="line">            <span class="comment">//shouldBeActive()再次检查是否observer活着</span></span><br><span class="line">            <span class="comment">//ObserverWrapper的实现类不止LifecycleBoundObserver, 这里暂时没看懂为什么再次检查</span></span><br><span class="line">            observer.activeStateChanged(<span class="literal">false</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (observer.mLastVersion &gt;= mVersion) &#123;</span><br><span class="line">            <span class="comment">//mVersion会在调用postValue()或者setValue()处+1</span></span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        observer.mLastVersion = mVersion;</span><br><span class="line">        observer.mObserver.onChanged((T) mData);</span><br><span class="line">        调用onChanged分发改变的数据</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>接着看postValue方法是是如果把值传到onChanged里：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"> </span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">postValue</span><span class="params">(T value)</span> &#123;</span><br><span class="line">    <span class="type">boolean</span> postTask;</span><br><span class="line">    <span class="keyword">synchronized</span> (mDataLock) &#123; </span><br><span class="line">        <span class="comment">// 一开始看到这里有点疑问，1. 为什么加锁, 1. 为什么要一个NOT_SET变量(NOT_SET是一个Obj对象)</span></span><br><span class="line">        <span class="comment">// 先带着疑问往下看</span></span><br><span class="line">        postTask = mPendingData == NOT_SET;</span><br><span class="line">        mPendingData = value;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!postTask) &#123; <span class="comment">// 先往下看，和加锁问题一起解释</span></span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);</span><br><span class="line">    <span class="comment">// mPostValueRunnable运行在主线程</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Runnable</span> <span class="variable">mPostValueRunnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        Object newValue;</span><br><span class="line">        <span class="keyword">synchronized</span> (mDataLock) &#123;  <span class="comment">//加锁, 和postValue里的锁是一样的</span></span><br><span class="line">            newValue = mPendingData;</span><br><span class="line">            mPendingData = NOT_SET;</span><br><span class="line">        &#125;</span><br><span class="line">        setValue((T) newValue);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;  </span><br><span class="line"></span><br><span class="line"><span class="meta">@MainThread</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">setValue</span><span class="params">(T value)</span> &#123;</span><br><span class="line">    assertMainThread(<span class="string">&quot;setValue&quot;</span>);</span><br><span class="line">    mVersion++; <span class="comment">// 版本号加1，保证mVersion&gt;mLastVersion</span></span><br><span class="line">    mData = value; <span class="comment">// 赋值给mData, 数据会分发到mObserver.onChanged</span></span><br><span class="line">    dispatchingValue(<span class="literal">null</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//到这里很清晰啦，现在回头看看postValue和mPostValueRunnable里的锁，和NOT_SET的作用</span></span><br><span class="line">    <span class="comment">//1. postValue方法可能会被很多条线程调用</span></span><br><span class="line">    <span class="comment">//2. mPostValueRunnable异步运行在主线程, 主线程是串联执行的，一次执行一个runnable</span></span><br><span class="line">    <span class="comment">//3. 不加锁很明显不行, 多线程调用时, value从postValue方法赋值给mPendingData, </span></span><br><span class="line">    <span class="comment">//在mPostValueRunnable主线程里把mPendingData赋值给mData, </span></span><br><span class="line">    <span class="comment">//mPendingData和mData都是指向同一个对象, mData在主线程都是指向了最新的对象。</span></span><br><span class="line">    <span class="comment">//在主线程队列的里mPostValueRunnable每次运行都是拿了最新的数据，有点多余</span></span><br><span class="line">    <span class="comment">//4. 假如去掉NOT_SET, value从postValue方法赋值给mPendingData, </span></span><br><span class="line">    <span class="comment">// 再到setValue的mData, mPendingData和mData都是指向同一个对象，</span></span><br><span class="line">    <span class="comment">// 虽然加了锁也没用。但有了NOT_SET就不同啦, mPendingData地址给mData，NOT_SET地址给mPendingData, </span></span><br><span class="line">    <span class="comment">// 在postValue方法的子线程里新进来的value地址又赋值给mPendingData。</span></span><br><span class="line">    <span class="comment">//5.看回postValue方法里 if (!postTask) &#123; return &#125; 这个地方，</span></span><br><span class="line">    <span class="comment">// 假如A线程调用了postValue, 把value赋值给mPendingData并释放了锁, </span></span><br><span class="line">    <span class="comment">// 把A.mPostValueRunnable放到主线程的Message中, </span></span><br><span class="line">    <span class="comment">// 但这时之前的B.mPostValueRunnable还没运行完，运行到setValue方法，也没有持有锁。</span></span><br><span class="line">    <span class="comment">// 这时B线程也调用了postValue, 这时A.mPostValueRunnable还没运行，没有把mPendingData=NOT_SET，</span></span><br><span class="line">    <span class="comment">// B线程postTask就为false了, 接着又把value赋值给mPendingData, 接着就直接return。</span></span><br><span class="line">    <span class="comment">// 等A.mPostValueRunnable运行时, mPendingData又被B线程更新啦。</span></span><br><span class="line">    <span class="comment">// if (!postTask) &#123; return &#125; 这个判断能减少主线程没必要mPostValueRunnable运行, </span></span><br><span class="line">    <span class="comment">// 而且能更新到最新的数据。</span></span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">dispatchingValue</span><span class="params">(<span class="meta">@Nullable</span> ObserverWrapper initiator)</span> &#123;</span><br><span class="line">    <span class="comment">// initiator是null , 下面是遍历mObservers存储ObserverWrapper通知所有观察者</span></span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">for</span> (Iterator&lt;Map.Entry&lt;Observer&lt;T&gt;, ObserverWrapper&gt;&gt; iterator =</span><br><span class="line">         mObservers.iteratorWithAdditions(); iterator.hasNext(); ) &#123;</span><br><span class="line">        considerNotify(iterator.next().getValue()); </span><br><span class="line">        <span class="comment">// considerNotify前面分析过了，看回LiveData.observe的过程, 最终分发数据Observer.onChanged</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="LiveData优点"><a href="#LiveData优点" class="headerlink" title="LiveData优点"></a>LiveData优点</h3><ul><li>UI和数据保持一致：遵循观察者模式，生命周期状态更改时，LiveData会通知Observer对象</li><li>避免内存泄漏：观察者绑定到Lifecycle对象，并在其相关生命周期走向Destroy后自行清理</li><li>不会因stop而崩溃：处于非活动状态，不会接收到LiveData事件，如Activity变为不可见</li><li>自动感知生命周期：LiveData自动管理在观察时意识到相关的生命周期状态变化。</li><li>数据始终保持最新：生命周期变为非活动状态再次变为活动状态时接收最新数据。 例如，Activity不可见变为可见时立即接收最新数据</li><li>解决Configuration changes问题：当屏幕旋转或者回收时，重新创建Activity或Fragment，可以即时收到数据变更</li><li>共享资源：使用单例扩展LiveData，就可在app中共享它</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;LiveData的定义&quot;&gt;&lt;a href=&quot;#LiveData的定义&quot; class=&quot;headerlink&quot; title=&quot;LiveData的定义&quot;&gt;&lt;/a&gt;LiveData的定义&lt;/h3&gt;&lt;p&gt;LiveData是一个可观察数据的持有者, 但是不像普通的可观察数据</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="Jetpack" scheme="https://adewu.github.io/tags/Jetpack/"/>
    
  </entry>
  
  <entry>
    <title>Flutter状态管理</title>
    <link href="https://adewu.github.io/2019/03/15/flutter_state_manage/"/>
    <id>https://adewu.github.io/2019/03/15/flutter_state_manage/</id>
    <published>2019-03-15T10:21:00.000Z</published>
    <updated>2024-02-12T12:18:06.701Z</updated>
    
    <content type="html"><![CDATA[<p>Flutter 中的状态管理是一个热门话题。为了选择适合需求的解决方案，实际上要做的第一件事就是明确这些需求，并设定一些目标。基于自身需求，我确定了这些：</p><ul><li>在不牺牲代码质量的情况下实现稳定的开发速度</li><li>将展示逻辑与业务逻辑分开</li><li>便于理解;结构层次明确。</li><li>具备普适性，满足绝大部分场景</li></ul><p>考虑到这些限制，我们将探索以下选项：</p><ul><li>使用 <code>setState()</code> 和 <code>StatefulWidgets</code>。 </li><li>ScopedModel 范围模型 </li><li>BLoC (Business Logic Component)</li><li>Redux</li></ul><h3 id="了解局部状态和全局状态之间的差异"><a href="#了解局部状态和全局状态之间的差异" class="headerlink" title="了解局部状态和全局状态之间的差异"></a>了解局部状态和全局状态之间的差异</h3><p>在深入探讨各种状态管理方案之前，理解什么是本地状态和全局状态将有助于我们更好地做出选择。为此，让我们来看一个实际例子：</p><p>为了更好地理解，让我们看一个实际例子：想象一个简单的登录表单，用户可以在其中输入用户名和密码，如果数据匹配，则从后端获取 “用户” 对象。在这个例子中，任何类型的登录表单验证都可以被视为局部状态，因为这些规则的范围只适用于这个组件，而我们应用程序的其他部分并不需要知道它。然而，从后端获得的 “用户” 对象可以被视为我们全局状态的一部分，因为它改变了整个应用程序的范围（未认证 vs. 认证），并且可能其他组件也依赖于它的数据。</p><h3 id="我的方案"><a href="#我的方案" class="headerlink" title="我的方案"></a>我的方案</h3><p>我的建议是使用 BLoC 模式进行本地状态管理，并依靠 Redux 来支持全局状态范围。特别是，如果这些是会随着时间的推移而增长的复杂应用程序。</p><h3 id="为什么不使用-setState-？"><a href="#为什么不使用-setState-？" class="headerlink" title="为什么不使用 setState() ？"></a>为什么不使用 <code>setState()</code> ？</h3><p>使用Widget内部的 <code>setState()</code> 开发很便利，可以获得即时反馈。然而，它并不能帮助我们实现目标：表示层和业务逻辑都属于同一个类，违反了代码整洁和质量的原则。随着应用程序的增长，代码维护将来可能会成为一个挑战，</p><h3 id="ScopedModel，朝着正确方向迈出的一步"><a href="#ScopedModel，朝着正确方向迈出的一步" class="headerlink" title="ScopedModel，朝着正确方向迈出的一步"></a>ScopedModel，朝着正确方向迈出的一步</h3><p>ScopedModel 是由 Brian Egan 维护的第三方包。它允许我们创建 <code>Model</code> 对象，并在我们认为有必要时使用名为 <code>notifyListeners()</code> 的方法；例如，我们模型中的任何属性更改。</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CounterModel</span> <span class="keyword">extends</span> <span class="title">Model</span> </span>&#123;</span><br><span class="line">  <span class="built_in">int</span> _counter = <span class="number">0</span>;</span><br><span class="line">  <span class="built_in">int</span> <span class="keyword">get</span> counter = _counter;</span><br><span class="line">  <span class="keyword">void</span> increment() &#123;</span><br><span class="line">    _counter++;</span><br><span class="line">    notifyListeners();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在我们的Widget中，我们可以使用 <code>ScopedModelDescendant</code> 对模型中的更改做出反应：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CounterApp</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123; </span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> ScopedModel&lt;CounterModel&gt;(</span><br><span class="line">      model: <span class="keyword">new</span> CounterModel(),</span><br><span class="line">      child: <span class="keyword">new</span> Column(children: [</span><br><span class="line">        <span class="keyword">new</span> ScopedModelDescendant&lt;CounterModel&gt;(</span><br><span class="line">          builder: (context, child, model) =&gt; <span class="keyword">new</span> Text(<span class="string">&#x27;<span class="subst">$&#123;model.counter&#125;</span>&#x27;</span>),</span><br><span class="line">        ),</span><br><span class="line">        <span class="keyword">new</span> Text(<span class="string">&quot;Another widget that doesn&#x27;t depend on the CounterModel&quot;</span>)</span><br><span class="line">      ])</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>与 <code>setState()</code> 相反，如果我们使用 <code>ScopedModel</code> 我们可以将业务逻辑与表示逻辑分开。然而，它存在一些限制：</p><ul><li>如果你的 <code>Model</code> 很复杂，那么了解何时正确调用 <code>notifyListeners()</code> 以避免不必要的更新可能会很困难。</li><li>公开的 API 并未准确描述一般 UI 应用程序的异步性质。</li></ul><p>有了所有这些信息，我不建议使用 <code>ScopedModel</code> ，除非您的状态非常容易管理；如果您的应用程序足够复杂，我不认为这是支持这种复杂性和增长的正确答案。</p><h3 id="BLoC，强大的解决方案"><a href="#BLoC，强大的解决方案" class="headerlink" title="BLoC，强大的解决方案"></a>BLoC，强大的解决方案</h3><p>BLoC 是 Google 推出并使用的一种模式；它将帮助我们实现以下目标：</p><ul><li>将业务逻辑与表示逻辑分开</li><li>支持UI应用的异步特性</li><li>可以在不同的 Dart 应用中重用，无论是 Flutter 应用还是 Angular Dart 应用。</li></ul><p>BLoC 实现的思路非常简单：</p><ul><li>BLoC 公开 <code>Sink&lt;I&gt;</code> API 来描述组件的异步输入</li><li>BLoC 公开 <code>Stream&lt;T&gt;</code> API 来描述组件的异步输出</li><li>最后，我们可以使用 <code>StreamBuilder</code> widget来管理数据流，从而消除了维护流订阅和重绘Widget树的工作。</li></ul><p>由于这是 Google 内部大量使用并推荐的方式，因此他们有非常好的示例，例如：<a href="https://github.com/filiph/state_experiments/tree/master/shared/lib/src/bloc_complex">https://github.com/filiph/state_experiments/tree/master/shared/lib/src/bloc_complex</a></p><p>建议在你的应用程序中使用 BLoC，特别是管理本地状态。即使对于全局状态管理，我相信这也可以是一个很好的解决方案；然而，您可能在这方面面临一些挑战，例如，在何处用正确的注入由不同 UI 组件访问的 BLoC，而这正是像 Redux 这样的解决方案的优势所在。</p><h3 id="Redux-和-BLoC，完美的组合"><a href="#Redux-和-BLoC，完美的组合" class="headerlink" title="Redux 和 BLoC，完美的组合"></a>Redux 和 BLoC，完美的组合</h3><p>我在文章开头描述的目标之一是寻找被广泛采用和可预测的东西。嗯，这就是 Redux。</p><p>Redux 是一种模式，结合一些工具，将帮助我们管理全局状态。它基于三个核心原则构建：：</p><ul><li>单一事实来源：整个应用程序的 <code>state</code> 存储在单个 <code>store</code> 内的对象树中。</li><li>状态是只读的：更改状态的唯一方法是发出 <code>action</code>，一个描述所发生情况的对象，即action。</li><li>纯函数修改状态： 为了指定状态如何被 action 转换，您需要编写纯函数reducer。</li></ul><p><img src="/../imgs/flutter_redux_intro.webp" alt="alt text" title="redux"></p><p>基于 Redux 的所有优点，推荐将其用于管理全局状态。但是，如果你希望应用可扩展，请确保您的 Redux 架构中没有引入任何局部状态更改。</p><h3 id="最后的想法"><a href="#最后的想法" class="headerlink" title="最后的想法"></a>最后的想法</h3><p>在选择管理状态的工具或模式时，没有绝对正确或错误的答案。最重要的是理解你的需求，选择最适合你的方案。</p><p>就我而言，结合 BLoC 和 Redux 的方式能够快速安全地扩展我的项目，同时由于这些工具在社区中广为人知，也能方便其他开发者的加入。然而，每个人的需求都不同，未来也可能会遇到问题或出现更好的工具。因此，保持好奇心，不断学习并思考什么是最适合你的解决方案才是最重要的。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Flutter 中的状态管理是一个热门话题。为了选择适合需求的解决方案，实际上要做的第一件事就是明确这些需求，并设定一些目标。基于自身需求，我确定了这些：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在不牺牲代码质量的情况下实现稳定的开发速度&lt;/li&gt;
&lt;li&gt;将展示逻辑与业务逻辑分开&lt;/l</summary>
      
    
    
    
    <category term="Flutter" scheme="https://adewu.github.io/categories/Flutter/"/>
    
    
    <category term="Flutter" scheme="https://adewu.github.io/tags/Flutter/"/>
    
  </entry>
  
  <entry>
    <title>Jetpack架构组件：ViewModel</title>
    <link href="https://adewu.github.io/2019/02/07/jetpack_viewModel/"/>
    <id>https://adewu.github.io/2019/02/07/jetpack_viewModel/</id>
    <published>2019-02-07T03:21:00.000Z</published>
    <updated>2024-02-12T08:43:43.634Z</updated>
    
    <content type="html"><![CDATA[<h3 id="ViewModel的定义"><a href="#ViewModel的定义" class="headerlink" title="ViewModel的定义"></a>ViewModel的定义</h3><p>根据Android官方文档，ViewModel旨在以生命周期可感知的方式存储和管理与UI相关的数据。它允许数据在屏幕旋转等配置更改中保留。</p><p><img src="/../imgs/mvvm.webp" alt="alt text"></p><p>ViewModel总是与作用域（一个Fragment或一个Activity）关联创建，并将保留直到其关联的Activity或Fragment永久处置，这意味着视图数据可以在Fragment&#x2F;Activity由于旋转而重新创建等事件中被保留下来。 ViewModel的主要职责包括：</p><ul><li>为Activity或Fragment准备和管理数据。</li><li>在配置更改期间保留数据，例如旋转。</li><li>处理Activity&#x2F;Fragment与程序其余部分的通信（例如调用业务逻辑类）。</li></ul><p>ViewModel将视图数据和逻辑的所有权与像Activities和Fragments这样与生命周期绑定的实体分离开来。ViewModel不仅消除了常见的生命周期问题，还有助于构建更模块化且更易于测试的UI。 Activity或Fragment可以通过LiveData或Android Data Binding观察ViewModel中的更改。 注意：ViewModel的唯一责任是管理UI的数据。它不应访问你的视图层次结构或持有对Activity或Fragment的引用。</p><p><img src="/../imgs/viewmodel_lifecycle.webp" alt="alt text" title="生命周期"></p><h3 id="ViewModel是如何在配置更改中保存数据的？"><a href="#ViewModel是如何在配置更改中保存数据的？" class="headerlink" title="ViewModel是如何在配置更改中保存数据的？"></a>ViewModel是如何在配置更改中保存数据的？</h3><p>首先，让我们查看Android文档，看看ViewModel是如何使用的。以下代码是一个示例。</p><p>获得ViewModel实例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MainActivity</span> <span class="keyword">extends</span> <span class="title class_">Activity</span> &#123;</span><br><span class="line"></span><br><span class="line">     <span class="meta">@Override</span></span><br><span class="line">     <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">onCreate</span><span class="params">(Bundle savedInstanceState)</span> &#123;</span><br><span class="line">         <span class="built_in">super</span>.onCreate(savedInstanceState);</span><br><span class="line">         setContentView(R.layout.activity_main);</span><br><span class="line">         </span><br><span class="line">         <span class="type">MyViewModel</span> <span class="variable">viewModel</span> <span class="operator">=</span> ViewModelProviders.of(<span class="built_in">this</span>).get(MyViewModel.class);</span><br><span class="line">         </span><br><span class="line">         viewModel.getUsers().observer(<span class="built_in">this</span>, <span class="keyword">new</span> <span class="title class_">Observer</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">             <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onChanged</span><span class="params">(<span class="meta">@Nullable</span> User data)</span> &#123;</span><br><span class="line">                 <span class="comment">// update the ui.</span></span><br><span class="line">             &#125;</span><br><span class="line">         &#125;);</span><br><span class="line">        </span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>编写ViewModel实现类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyViewModel</span> <span class="keyword">extends</span> <span class="title class_">ViewModel</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> MutableLiveData&lt;List&lt;User&gt;&gt; users;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> LiveData&lt;List&lt;User&gt;&gt; <span class="title function_">getUsers</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (users == <span class="literal">null</span>) &#123;</span><br><span class="line">            users = <span class="keyword">new</span> <span class="title class_">MutableLiveData</span>&lt;List&lt;User&gt;&gt;();</span><br><span class="line">            loadUsers();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> users;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">loadUsers</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// perform an asynchronous operation to fetch users.</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/../imgs/viewmodel_uml.webp" alt="alt text" title="类图"></p><p>让我们深入了解每个类及其方法，从ViewModelProviders.of开始：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Utilities methods for &#123;<span class="doctag">@link</span> ViewModelStore&#125; class.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ViewModelProviders</span> &#123;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Creates a &#123;<span class="doctag">@link</span> ViewModelProvider&#125;, which retains ViewModels while a scope of given Activity</span></span><br><span class="line"><span class="comment">     * is alive. More detailed explanation is in &#123;<span class="doctag">@link</span> ViewModel&#125;.</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;</span></span><br><span class="line"><span class="comment">     * It uses &#123;<span class="doctag">@link</span> ViewModelProvider.AndroidViewModelFactory&#125; to instantiate new ViewModels.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> activity an activity, in whose scope ViewModels should be retained</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> a ViewModelProvider instance</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@NonNull</span></span><br><span class="line">    <span class="meta">@MainThread</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> ViewModelProvider <span class="title function_">of</span><span class="params">(<span class="meta">@NonNull</span> FragmentActivity activity)</span> &#123;</span><br><span class="line">        ViewModelProvider.<span class="type">AndroidViewModelFactory</span> <span class="variable">factory</span> <span class="operator">=</span></span><br><span class="line">                ViewModelProvider.AndroidViewModelFactory.getInstance(</span><br><span class="line">                        checkApplication(activity));</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ViewModelProvider</span>(ViewModelStores.of(activity), factory);</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>ViewModelProviders.of只是一个ViewModelProvider的工厂，它依赖于ViewModelFactory和ViewModelStore。</p><h4 id="ViewModelFactory"><a href="#ViewModelFactory" class="headerlink" title="ViewModelFactory"></a>ViewModelFactory</h4><p>ViewModelProviders.of(this).get(MyViewModel.class)使用反射来实例化传递给它的ViewModel类。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Simple factory, which calls empty constructor on the give class.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">NewInstanceFactory</span> <span class="keyword">implements</span> <span class="title class_">Factory</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@SuppressWarnings(&quot;ClassNewInstance&quot;)</span></span><br><span class="line">        <span class="meta">@NonNull</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> &lt;T <span class="keyword">extends</span> <span class="title class_">ViewModel</span>&gt; T <span class="title function_">create</span><span class="params">(<span class="meta">@NonNull</span> Class&lt;T&gt; modelClass)</span> &#123;</span><br><span class="line">            <span class="comment">//noinspection TryWithIdenticalCatches</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> modelClass.newInstance();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InstantiationException e) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;Cannot create an instance of &quot;</span> + modelClass, e);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IllegalAccessException e) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;Cannot create an instance of &quot;</span> + modelClass, e);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>如果ViewModel类是AndroidViewModel类型，则会创建一个新实例，并将Application实例作为构造函数中的参数传递进来。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * &#123;<span class="doctag">@link</span> Factory&#125; which may create &#123;<span class="doctag">@link</span> AndroidViewModel&#125; and</span></span><br><span class="line"><span class="comment">     * &#123;<span class="doctag">@link</span> ViewModel&#125;, which have an empty constructor.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AndroidViewModelFactory</span> <span class="keyword">extends</span> <span class="title class_">ViewModelProvider</span>.NewInstanceFactory &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> AndroidViewModelFactory sInstance;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Retrieve a singleton instance of AndroidViewModelFactory.</span></span><br><span class="line"><span class="comment">         *</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@param</span> application an application to pass in &#123;<span class="doctag">@link</span> AndroidViewModel&#125;</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@return</span> A valid &#123;<span class="doctag">@link</span> AndroidViewModelFactory&#125;</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> AndroidViewModelFactory <span class="title function_">getInstance</span><span class="params">(<span class="meta">@NonNull</span> Application application)</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (sInstance == <span class="literal">null</span>) &#123;</span><br><span class="line">                sInstance = <span class="keyword">new</span> <span class="title class_">AndroidViewModelFactory</span>(application);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> sInstance;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> Application mApplication;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Creates a &#123;<span class="doctag">@code</span> AndroidViewModelFactory&#125;</span></span><br><span class="line"><span class="comment">         *</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@param</span> application an application to pass in &#123;<span class="doctag">@link</span> AndroidViewModel&#125;</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">AndroidViewModelFactory</span><span class="params">(<span class="meta">@NonNull</span> Application application)</span> &#123;</span><br><span class="line">            mApplication = application;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@NonNull</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> &lt;T <span class="keyword">extends</span> <span class="title class_">ViewModel</span>&gt; T <span class="title function_">create</span><span class="params">(<span class="meta">@NonNull</span> Class&lt;T&gt; modelClass)</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (AndroidViewModel.class.isAssignableFrom(modelClass)) &#123;</span><br><span class="line">                <span class="comment">//noinspection TryWithIdenticalCatches</span></span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">return</span> modelClass.getConstructor(Application.class).newInstance(mApplication);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (NoSuchMethodException e) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;Cannot create an instance of &quot;</span> + modelClass, e);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IllegalAccessException e) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;Cannot create an instance of &quot;</span> + modelClass, e);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InstantiationException e) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;Cannot create an instance of &quot;</span> + modelClass, e);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InvocationTargetException e) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;Cannot create an instance of &quot;</span> + modelClass, e);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">super</span>.create(modelClass);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h4 id="创建自定义ViewModelProviderFactory"><a href="#创建自定义ViewModelProviderFactory" class="headerlink" title="创建自定义ViewModelProviderFactory"></a>创建自定义ViewModelProviderFactory</h4><p>你也可以创建一个自定义工厂类，继承一个 ViewModelFactory 的泛型类，并在其中初始化你的 ViewModel 类，根据需要传递数据进行初始化。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyViewModelFactory</span> <span class="keyword">extends</span> <span class="title class_">ViewModelProvider</span>.NewInstanceFactory &#123;</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">private</span> String data1;</span><br><span class="line">    <span class="keyword">private</span> String data2;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyViewModelFactory</span><span class="params">(String data1, String data2)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.data1 = data1;</span><br><span class="line">        <span class="built_in">this</span>.data2 = data2;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="meta">@NonNull</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> &lt;T <span class="keyword">extends</span> <span class="title class_">ViewModel</span>&gt; T <span class="title function_">create</span><span class="params">(<span class="meta">@NonNull</span> Class&lt;T&gt; modelClass)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (MyViewModel.class.isAssignableFrom(modelClass)) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> modelClass.getConstructor(MyViewModel.class).newInstance(data1, data2);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InstantiationException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IllegalAccessException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InvocationTargetException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (NoSuchMethodException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>.create(modelClass);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>你可以在请求ViewModel实例时将上面创建的工厂传递给ViewModelProviders类。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//initializing the custom factory class </span></span><br><span class="line"><span class="type">MyViewModelFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyViewModelFactory</span>(data1, data2);</span><br><span class="line">ViewModelProviders.of(<span class="built_in">this</span>, factory).get(MyViewModel.class);</span><br></pre></td></tr></table></figure><p>注意：如果ViewModel类是AndroidViewModel类型，则会创建一个新实例，并将应用作为构造函数中的单个参数传递，否则将调用父实现。</p><h4 id="ViewModelStores"><a href="#ViewModelStores" class="headerlink" title="ViewModelStores"></a>ViewModelStores</h4><p>这个类的作用是对HolderFragmentManager内的holderFragmentOf（Activity&#x2F;Fragment）静态方法的调用进行抽象化。这个方法将负责返回一个ViewModelStoreOwner。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Factory methods for &#123;<span class="doctag">@link</span> ViewModelStore&#125; class.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SuppressWarnings(&quot;WeakerAccess&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ViewModelStores</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">ViewModelStores</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns the &#123;<span class="doctag">@link</span> ViewModelStore&#125; of the given activity.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> activity an activity whose &#123;<span class="doctag">@code</span> ViewModelStore&#125; is requested</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> a &#123;<span class="doctag">@code</span> ViewModelStore&#125;</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@NonNull</span></span><br><span class="line">    <span class="meta">@MainThread</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> ViewModelStore <span class="title function_">of</span><span class="params">(<span class="meta">@NonNull</span> FragmentActivity activity)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (activity <span class="keyword">instanceof</span> ViewModelStoreOwner) &#123;</span><br><span class="line">            <span class="keyword">return</span> ((ViewModelStoreOwner) activity).getViewModelStore();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> holderFragmentFor(activity).getViewModelStore();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns the &#123;<span class="doctag">@link</span> ViewModelStore&#125; of the given fragment.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> fragment a fragment whose &#123;<span class="doctag">@code</span> ViewModelStore&#125; is requested</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> a &#123;<span class="doctag">@code</span> ViewModelStore&#125;</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@NonNull</span></span><br><span class="line">    <span class="meta">@MainThread</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> ViewModelStore <span class="title function_">of</span><span class="params">(<span class="meta">@NonNull</span> Fragment fragment)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (fragment <span class="keyword">instanceof</span> ViewModelStoreOwner) &#123;</span><br><span class="line">            <span class="keyword">return</span> ((ViewModelStoreOwner) fragment).getViewModelStore();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> holderFragmentFor(fragment).getViewModelStore();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>ViewModelStores.of似乎类似于ViewModelProviders.of方法，在需要时创建ViewModelStore的新实例。</p><h4 id="ViewModelStore"><a href="#ViewModelStore" class="headerlink" title="ViewModelStore"></a>ViewModelStore</h4><p>一个简单的存储器，其中包含一个HashMap&lt;String, ViewModel&gt;，其中键是视图模型的类名，值是ViewModel本身。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Class to store &#123;<span class="doctag">@code</span> ViewModels&#125;.</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * An instance of &#123;<span class="doctag">@code</span> ViewModelStore&#125; must be retained through configuration changes:</span></span><br><span class="line"><span class="comment"> * if an owner of this &#123;<span class="doctag">@code</span> ViewModelStore&#125; is destroyed and recreated due to configuration</span></span><br><span class="line"><span class="comment"> * changes, new instance of an owner should still have the same old instance of</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@code</span> ViewModelStore&#125;.</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * If an owner of this &#123;<span class="doctag">@code</span> ViewModelStore&#125; is destroyed and is not going to be recreated,</span></span><br><span class="line"><span class="comment"> * then it should call &#123;<span class="doctag">@link</span> #clear()&#125; on this &#123;<span class="doctag">@code</span> ViewModelStore&#125;, so &#123;<span class="doctag">@code</span> ViewModels&#125; would</span></span><br><span class="line"><span class="comment"> * be notified that they are no longer used.</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@link</span> android.arch.lifecycle.ViewModelStores&#125; provides a &#123;<span class="doctag">@code</span> ViewModelStore&#125; for</span></span><br><span class="line"><span class="comment"> * activities and fragments.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ViewModelStore</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> HashMap&lt;String, ViewModel&gt; mMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(String key, ViewModel viewModel)</span> &#123;</span><br><span class="line">        <span class="type">ViewModel</span> <span class="variable">oldViewModel</span> <span class="operator">=</span> mMap.get(key);</span><br><span class="line">        <span class="keyword">if</span> (oldViewModel != <span class="literal">null</span>) &#123;</span><br><span class="line">            oldViewModel.onCleared();</span><br><span class="line">        &#125;</span><br><span class="line">        mMap.put(key, viewModel);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> ViewModel <span class="title function_">get</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> mMap.get(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  Clears internal storage and notifies ViewModels that they are no longer used.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">clear</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (ViewModel vm : mMap.values()) &#123;</span><br><span class="line">            vm.onCleared();</span><br><span class="line">        &#125;</span><br><span class="line">        mMap.clear();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="ViewModelStoreOwner"><a href="#ViewModelStoreOwner" class="headerlink" title="ViewModelStoreOwner"></a>ViewModelStoreOwner</h4><p>顾名思义，是ViewModelStore的所有者。这可以是任何实现了该接口定义的getViewModelStore()方法的类。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * A scope that owns &#123;<span class="doctag">@link</span> ViewModelStore&#125;.</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * A responsibility of an implementation of this interface is to retain owned ViewModelStore</span></span><br><span class="line"><span class="comment"> * during the configuration changes and call &#123;<span class="doctag">@link</span> ViewModelStore#clear()&#125;, when this scope is</span></span><br><span class="line"><span class="comment"> * going to be destroyed.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SuppressWarnings(&quot;WeakerAccess&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">ViewModelStoreOwner</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns owned &#123;<span class="doctag">@link</span> ViewModelStore&#125;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> a &#123;<span class="doctag">@code</span> ViewModelStore&#125;</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@NonNull</span></span><br><span class="line">    ViewModelStore <span class="title function_">getViewModelStore</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在该库中，ViewModelStoreOwner即是HolderFragment。该类具有一个ViewModelStore变量，可以通过getViewModelStore()方法访问。</p><h4 id="HolderFragment"><a href="#HolderFragment" class="headerlink" title="HolderFragment"></a>HolderFragment</h4><p>HolderFragment是一个普通的Android无界面Fragment。它充当一个容器，在它的 <code>ViewModelStore</code> 中存储所有活动周期内的 ViewModel 对象。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HolderFragment</span> <span class="keyword">extends</span> <span class="title class_">Fragment</span> <span class="keyword">implements</span> <span class="title class_">ViewModelStoreOwner</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">LOG_TAG</span> <span class="operator">=</span> <span class="string">&quot;ViewModelStores&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">HolderFragmentManager</span> <span class="variable">sHolderFragmentManager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HolderFragmentManager</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">HOLDER_TAG</span> <span class="operator">=</span></span><br><span class="line">            <span class="string">&quot;android.arch.lifecycle.state.StateProviderHolderFragment&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">ViewModelStore</span> <span class="variable">mViewModelStore</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ViewModelStore</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">HolderFragment</span><span class="params">()</span> &#123;</span><br><span class="line">        setRetainInstance(<span class="literal">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onCreate</span><span class="params">(<span class="meta">@Nullable</span> Bundle savedInstanceState)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        sHolderFragmentManager.holderFragmentCreated(<span class="built_in">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onSaveInstanceState</span><span class="params">(Bundle outState)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>.onSaveInstanceState(outState);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onDestroy</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>.onDestroy();</span><br><span class="line">        mViewModelStore.clear();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@NonNull</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> ViewModelStore <span class="title function_">getViewModelStore</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> mViewModelStore;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> HolderFragment <span class="title function_">holderFragmentFor</span><span class="params">(FragmentActivity activity)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sHolderFragmentManager.holderFragmentFor(activity);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> HolderFragment <span class="title function_">holderFragmentFor</span><span class="params">(Fragment fragment)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sHolderFragmentManager.holderFragmentFor(fragment);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;WeakerAccess&quot;)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">HolderFragmentManager</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> Map&lt;Activity, HolderFragment&gt; mNotCommittedActivityHolders = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">private</span> Map&lt;Fragment, HolderFragment&gt; mNotCommittedFragmentHolders = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="type">ActivityLifecycleCallbacks</span> <span class="variable">mActivityCallbacks</span> <span class="operator">=</span></span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">EmptyActivityLifecycleCallbacks</span>() &#123;</span><br><span class="line">                    <span class="meta">@Override</span></span><br><span class="line">                    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onActivityDestroyed</span><span class="params">(Activity activity)</span> &#123;</span><br><span class="line">                        <span class="type">HolderFragment</span> <span class="variable">fragment</span> <span class="operator">=</span> mNotCommittedActivityHolders.remove(activity);</span><br><span class="line">                        <span class="keyword">if</span> (fragment != <span class="literal">null</span>) &#123;</span><br><span class="line">                            Log.e(LOG_TAG, <span class="string">&quot;Failed to save a ViewModel for &quot;</span> + activity);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="type">boolean</span> <span class="variable">mActivityCallbacksIsAdded</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="type">FragmentLifecycleCallbacks</span> <span class="variable">mParentDestroyedCallback</span> <span class="operator">=</span></span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">FragmentLifecycleCallbacks</span>() &#123;</span><br><span class="line">                    <span class="meta">@Override</span></span><br><span class="line">                    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onFragmentDestroyed</span><span class="params">(FragmentManager fm, Fragment parentFragment)</span> &#123;</span><br><span class="line">                        <span class="built_in">super</span>.onFragmentDestroyed(fm, parentFragment);</span><br><span class="line">                        <span class="type">HolderFragment</span> <span class="variable">fragment</span> <span class="operator">=</span> mNotCommittedFragmentHolders.remove(</span><br><span class="line">                                parentFragment);</span><br><span class="line">                        <span class="keyword">if</span> (fragment != <span class="literal">null</span>) &#123;</span><br><span class="line">                            Log.e(LOG_TAG, <span class="string">&quot;Failed to save a ViewModel for &quot;</span> + parentFragment);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">void</span> <span class="title function_">holderFragmentCreated</span><span class="params">(Fragment holderFragment)</span> &#123;</span><br><span class="line">            <span class="type">Fragment</span> <span class="variable">parentFragment</span> <span class="operator">=</span> holderFragment.getParentFragment();</span><br><span class="line">            <span class="keyword">if</span> (parentFragment != <span class="literal">null</span>) &#123;</span><br><span class="line">                mNotCommittedFragmentHolders.remove(parentFragment);</span><br><span class="line">                parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(</span><br><span class="line">                        mParentDestroyedCallback);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                mNotCommittedActivityHolders.remove(holderFragment.getActivity());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> HolderFragment <span class="title function_">findHolderFragment</span><span class="params">(FragmentManager manager)</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (manager.isDestroyed()) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;Can&#x27;t access ViewModels from onDestroy&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="type">Fragment</span> <span class="variable">fragmentByTag</span> <span class="operator">=</span> manager.findFragmentByTag(HOLDER_TAG);</span><br><span class="line">            <span class="keyword">if</span> (fragmentByTag != <span class="literal">null</span> &amp;&amp; !(fragmentByTag <span class="keyword">instanceof</span> HolderFragment)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;Unexpected &quot;</span></span><br><span class="line">                        + <span class="string">&quot;fragment instance was returned by HOLDER_TAG&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> (HolderFragment) fragmentByTag;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> HolderFragment <span class="title function_">createHolderFragment</span><span class="params">(FragmentManager fragmentManager)</span> &#123;</span><br><span class="line">            <span class="type">HolderFragment</span> <span class="variable">holder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HolderFragment</span>();</span><br><span class="line">            fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();</span><br><span class="line">            <span class="keyword">return</span> holder;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        HolderFragment <span class="title function_">holderFragmentFor</span><span class="params">(FragmentActivity activity)</span> &#123;</span><br><span class="line">            <span class="type">FragmentManager</span> <span class="variable">fm</span> <span class="operator">=</span> activity.getSupportFragmentManager();</span><br><span class="line">            <span class="type">HolderFragment</span> <span class="variable">holder</span> <span class="operator">=</span> findHolderFragment(fm);</span><br><span class="line">            <span class="keyword">if</span> (holder != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> holder;</span><br><span class="line">            &#125;</span><br><span class="line">            holder = mNotCommittedActivityHolders.get(activity);</span><br><span class="line">            <span class="keyword">if</span> (holder != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> holder;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (!mActivityCallbacksIsAdded) &#123;</span><br><span class="line">                mActivityCallbacksIsAdded = <span class="literal">true</span>;</span><br><span class="line">                activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);</span><br><span class="line">            &#125;</span><br><span class="line">            holder = createHolderFragment(fm);</span><br><span class="line">            mNotCommittedActivityHolders.put(activity, holder);</span><br><span class="line">            <span class="keyword">return</span> holder;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        HolderFragment <span class="title function_">holderFragmentFor</span><span class="params">(Fragment parentFragment)</span> &#123;</span><br><span class="line">            <span class="type">FragmentManager</span> <span class="variable">fm</span> <span class="operator">=</span> parentFragment.getChildFragmentManager();</span><br><span class="line">            <span class="type">HolderFragment</span> <span class="variable">holder</span> <span class="operator">=</span> findHolderFragment(fm);</span><br><span class="line">            <span class="keyword">if</span> (holder != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> holder;</span><br><span class="line">            &#125;</span><br><span class="line">            holder = mNotCommittedFragmentHolders.get(parentFragment);</span><br><span class="line">            <span class="keyword">if</span> (holder != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> holder;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            parentFragment.getFragmentManager()</span><br><span class="line">                    .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, <span class="literal">false</span>);</span><br><span class="line">            holder = createHolderFragment(fm);</span><br><span class="line">            mNotCommittedFragmentHolders.put(parentFragment, holder);</span><br><span class="line">            <span class="keyword">return</span> holder;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="谁持有HolderFragment？"><a href="#谁持有HolderFragment？" class="headerlink" title="谁持有HolderFragment？"></a>谁持有HolderFragment？</h3><h4 id="HolderFragmentManager"><a href="#HolderFragmentManager" class="headerlink" title="HolderFragmentManager"></a>HolderFragmentManager</h4><p>HolderFragment有一个名为HolderFragmentManager的内部静态类。HolderFragmentManager创建和管理HolderFragment实例。<br>创建实例后，将它们关联到一个Activity或Fragment。<br>整个过程使用holderFragmentFor(Activity)和holderFragmentFor(Fragment)方法完成。<br>如果没有HolderFragment的实例，这些方法将：</p><ul><li>创建一个HolderFragment的实例。</li><li>将新实例添加到父(Activity&#x2F;Fragment)FragmentManager。这将导致HolderFragment内部ViewModel的作用范围扩大。</li><li>向Activity&#x2F;Fragment的生命周期注册一个回调函数onDestroy()。因为HolderFragment位于Activity&#x2F;Fragment的FragmentManager中，所以当Activity&#x2F;Fragment被销毁时，其onDestroy()方法将被调用，并且ViewModelStore将被清除。最后，当注册的回调被调用时，HolderFragment实例将从与其Activity&#x2F;Fragment关联的HolderFragmentManager的HashMap中移除。</li><li>将holder Fragment添加到一个HashMap中，其中键是Activity&#x2F;Fragment。</li><li>返回HolderFragment实例。</li></ul><p>当已经存在HolderFragment的实例时，这些方法将查找并返回已经在HashMap中的实例。</p><h4 id="HolderFragment如何保持状态？"><a href="#HolderFragment如何保持状态？" class="headerlink" title="HolderFragment如何保持状态？"></a>HolderFragment如何保持状态？</h4><p>通过将保留实例设置为true并不提供视图，HolderFragment本质上变成了一个无界面Fragment，在Activity未被销毁的情况下会被保留。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">HolderFragment</span><span class="params">()</span> &#123;</span><br><span class="line">   setRetainInstance(<span class="literal">true</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>控制一个Fragment实例是否在Activity重新创建（如配置更改）时保留。这只能用于不在返回栈中的Fragment。如果设置了这个选项，在Activity重新创建时Fragment的生命周期会稍有不同：</p><ul><li>onDestroy()不会被调用（但onDetach()仍会被调用，因为Fragment正在从当前Activity中分离）。</li><li>由于Fragment没有被重新创建，onCreate(Bundle)不会被调用。</li><li>onAttach(Activity)和onActivityCreated(Bundle)仍会被调用。</li></ul><h4 id="获取ViewModel实例"><a href="#获取ViewModel实例" class="headerlink" title="获取ViewModel实例"></a>获取ViewModel实例</h4><p>由于我们对ViewModelProvider的创建和其依赖关系有了基本的了解，我们现在将深入探讨它如何创建和检索ViewModel实例，并在整个配置更改过程中保持它们。让我们来看一下方法调用：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">get(MyViewModel.class)</span><br></pre></td></tr></table></figure><p>它尝试从Map中检索一个MyViewModel实例。如果没有找到，则使用工厂来创建它，然后将其存储到HashMap&lt;String, ViewModel&gt;中。为了检索已创建的ViewModel，键被命名为类的名字。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>在本文中，我探讨了新的ViewModel类的基础知识。主要包括：</p><ul><li>ViewModel类旨在以生命周期感知的方式保存和管理与UI相关的数据。这使得数据能够在配置更改（如屏幕旋转）中存活。</li><li>ViewModel将UI实现与应用程序数据分离。</li><li>ViewModel的生命周期感知自关联的UI控制器首次创建之时，直到完全销毁。</li><li>永远不要直接或间接地将UI控制器或Context存储在ViewModel中。这包括在ViewModel中存储View。直接或间接引用UI控制器会破坏将UI与数据分离的目的，并可能导致内存泄漏。</li><li>HolderFragment是一个无界面Fragment（没有UI），通过setRetainInstance(true)添加到Fragment堆栈中。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;ViewModel的定义&quot;&gt;&lt;a href=&quot;#ViewModel的定义&quot; class=&quot;headerlink&quot; title=&quot;ViewModel的定义&quot;&gt;&lt;/a&gt;ViewModel的定义&lt;/h3&gt;&lt;p&gt;根据Android官方文档，ViewModel旨在以生命周期</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="Jetpack" scheme="https://adewu.github.io/tags/Jetpack/"/>
    
  </entry>
  
  <entry>
    <title>Jetpack架构组件：LifeCycle</title>
    <link href="https://adewu.github.io/2019/01/10/jetpack_lifecycle/"/>
    <id>https://adewu.github.io/2019/01/10/jetpack_lifecycle/</id>
    <published>2019-01-10T11:21:00.000Z</published>
    <updated>2024-02-12T03:19:49.687Z</updated>
    
    <content type="html"><![CDATA[<p>LifeCycle 组件与 Android 组件（如 Activity 或 Fragment）的生命周期事件相关，它包含三个主要的类：</p><ul><li><strong>LifeCycle（生命周期）</strong></li><li><strong>LifeCycleOwner（生命周期所有者）</strong></li><li><strong>LifeCycleObserver（生命周期观察者）</strong></li></ul><h3 id="LifeCycle"><a href="#LifeCycle" class="headerlink" title="LifeCycle"></a>LifeCycle</h3><p>一个 LifeCycle 对象可以获取与 LifeCycleOwner（通常是 Activity 或 Fragment）当前生命周期相关的信息。</p><p>LifeCycle 对象提供了关于所有者生命周期的信息，以事件和状态的形式。</p><p>事件是与生命周期所有者事件相关的，如 onCreate()、onStart()、onStop() 等。</p><p><img src="/../imgs/lifecycle_timeline.webp" alt="alt text" title="生命周期时间线"></p><p>从上面的时间线图中，我们注意到事件 ON_CREATE、ON_START 和 ON_RESUME 在相应的 Activity（LifeCycleOwner）的 onCreate()、onStart() 和 onResume() 返回后立即分派。</p><p>另外，我们还注意到有一个特殊的事件：ON_ANY，它匹配所有的生命周期所有者事件。</p><p>状态如文档所述：</p><p>生命周期状态。你可以将状态视为图中的节点，将 Lifecycle.Events 视为这些节点之间的边。</p><p>我也喜欢把它们看作是描述生命周期所有者在某一时刻的生命周期状态的值。</p><p><img src="/../imgs/lifecycle_state_timeline.webp" alt="alt text" title="生命周期状态时间线"></p><p>从上面的示例中，我们从 INITIALIZED 状态开始，它代表了生命周期所有者已经被构建，但尚未接收到其 onCreate() 方法的状态。</p><p>我们还注意到一个状态可以跨越多个生命周期所有者事件，因此对于一个 Activity 来说，一旦创建完成并且在暂停之前，它就被认为处于 CREATED 状态，在活动的 onStart() 和 onPause() 之间的时间段也被认为是处于 STARTED 状态。</p><p>由于在给定的时间点上可以交替使用多个状态，如果我们想要检查特定的状态，我们总是使用 isAtLeast 方法：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (lifeCycle.currentState.isAtLeast(Lifecycle.State.STARTED)) &#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="LifeCycleOwner"><a href="#LifeCycleOwner" class="headerlink" title="LifeCycleOwner"></a>LifeCycleOwner</h3><p>任何实现 LifeCycleOwner 接口的类都表示它具有 Android 生命周期。</p><p>例如，从支持库 26.1.0 开始，Fragments 和 Activities 实现了 LifeCycleOwner 接口。</p><p>我们可以通过实现该接口并使用 LifeCycleRegistry 来创建自定义的 LifeCycleOwner 组件。</p><h3 id="LifeCycleObserver"><a href="#LifeCycleObserver" class="headerlink" title="LifeCycleObserver"></a>LifeCycleObserver</h3><p>它是一个标记接口，表示实现该接口的类是一个 LifeCycleObserver。与 LifeCycle 事件的交互可以通过两种方式进行：</p><ul><li>如果我们使用 Java 8，观察者类可以实现 DefaultLifecycleObserver 接口（该接口又实现了 LifecycleObserver 接口），它定义了生命周期事件作为默认方法，因此我们可以重写我们感兴趣的事件方法：</li></ul><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyLifeCycleObserver</span> : <span class="type">DefaultLifecycleObserver</span> &#123;</span><br><span class="line">     </span><br><span class="line">     <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onCreate</span><span class="params">(owner: <span class="type">LifecycleOwner</span>)</span></span> &#123;</span><br><span class="line">         <span class="comment">//...</span></span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><ul><li>否则，我们可以直接实现 LifecycleObserver 接口，并使用 OnLifecycleEvent 注解：</li></ul><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyLifeCycleObserver</span>(<span class="keyword">val</span> lifeCycle: Lifecycle) : LifecycleObserver &#123;</span><br><span class="line"><span class="meta">@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)</span></span><br><span class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">onCreated</span><span class="params">()</span></span> &#123;</span><br><span class="line">  <span class="comment">//...</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>现在我们将实现一个简单的demo来展示如何使用 LifeCycle 组件，这将是一个非常简单的应用程序，记录我们的主 Activity 经历的不同生命周期事件。</p><h3 id="Demo"><a href="#Demo" class="headerlink" title="Demo"></a>Demo</h3><p>首先我们创建一个日志类：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyLogger</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">companion</span> <span class="keyword">object</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">val</span> TAG = <span class="string">&quot;MyLogger&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logCreate</span><span class="params">()</span></span> &#123;</span><br><span class="line">        Log.d(TAG, <span class="string">&quot;Activity created&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logStart</span><span class="params">()</span></span> &#123;</span><br><span class="line">        Log.d(TAG, <span class="string">&quot;Activity started&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logResume</span><span class="params">()</span></span> &#123;</span><br><span class="line">        Log.d(TAG, <span class="string">&quot;Activity resumed&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logPause</span><span class="params">()</span></span> &#123;</span><br><span class="line">        Log.d(TAG, <span class="string">&quot;Activity will pause&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logStop</span><span class="params">()</span></span> &#123;</span><br><span class="line">        Log.d(TAG, <span class="string">&quot;Activity will stop&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logDestroy</span><span class="params">()</span></span> &#123;</span><br><span class="line">        Log.d(TAG, <span class="string">&quot;Activity will be destroyed&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后，我们将创建一个 LifeCycleObserver 类，它包装了日志记录器以记录不同的生命周期事件：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyLifeCycleObserver</span>(<span class="keyword">private</span> <span class="keyword">val</span> lifeCycle: Lifecycle, <span class="keyword">private</span> <span class="keyword">val</span> logger: MyLogger) : LifecycleObserver &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logCreate</span><span class="params">()</span></span> &#123;</span><br><span class="line">        logger.logCreate()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@OnLifecycleEvent(Lifecycle.Event.ON_START)</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logStart</span><span class="params">()</span></span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (lifeCycle.currentState.isAtLeast(Lifecycle.State.STARTED)) &#123;</span><br><span class="line">            logger.logStart()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logResume</span><span class="params">()</span></span> &#123;</span><br><span class="line">        logger.logResume()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logPause</span><span class="params">()</span></span> &#123;</span><br><span class="line">        logger.logPause()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@OnLifecycleEvent(Lifecycle.Event.ON_STOP)</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logStop</span><span class="params">()</span></span> &#123;</span><br><span class="line">        logger.logStop()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">logDestroy</span><span class="params">()</span></span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (lifeCycle.currentState.isAtLeast(Lifecycle.State.DESTROYED)) &#123;</span><br><span class="line">            logger.logDestroy()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在这里，我们通过 @OnLifecycleEvent 注解与 LifeCycle 事件进行交互。</p><p>然后，在我们的主 Activity（即我们的 LifeCycleOwner）中，我们将观察者添加到活动的生命周期观察者中：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MainActivity</span>: <span class="type">AppCompatActivity</span>() &#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onCreate</span><span class="params">(savedInstanceState: <span class="type">Bundle</span> ? )</span></span> &#123;</span><br><span class="line"><span class="keyword">super</span>.onCreate(savedInstanceState)</span><br><span class="line">setContentView(R.layout.activity_main)</span><br><span class="line"></span><br><span class="line"><span class="keyword">val</span> lifeCycleObserver = MyLifeCycleObserver(lifecycle)</span><br><span class="line">lifecycle.addObserver(lifeCycleObserver)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后进行单元测试</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyLifeCycleObserverTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">lateinit</span> <span class="keyword">var</span> lifeCycleObserver: MyLifeCycleObserver</span><br><span class="line">    <span class="keyword">lateinit</span> <span class="keyword">var</span> lifeCycle: LifecycleRegistry</span><br><span class="line">    <span class="keyword">val</span> logger = mock(MyLogger::<span class="keyword">class</span>.java)</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">setUp</span><span class="params">()</span></span> &#123;</span><br><span class="line">        <span class="keyword">val</span> lifeCycleOwner: LifecycleOwner = mock(LifecycleOwner::<span class="keyword">class</span>.java)</span><br><span class="line">        lifeCycle = LifecycleRegistry(lifeCycleOwner)</span><br><span class="line">        lifeCycleObserver = MyLifeCycleObserver(lifeCycle, logger)</span><br><span class="line">        lifeCycle.addObserver(lifeCycleObserver)</span><br><span class="line"></span><br><span class="line">        lifeCycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">shouldLogStart</span><span class="params">()</span></span> &#123;</span><br><span class="line">        lifeCycle.markState(Lifecycle.State.STARTED)</span><br><span class="line">        verify(logger).logStart()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">fun</span> <span class="title">shouldLogStop</span><span class="params">()</span></span> &#123;</span><br><span class="line">        lifeCycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)</span><br><span class="line">        lifeCycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP)</span><br><span class="line">        verify(logger).logStop()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在这里，我们使用了一个LifeCycleRegistry作为LifeCycleOwner，在测试onStop()生命周期事件时，我们必须将生命周期移动到先前的状态（onResume()）然后再将其置于ON_STOP状态，这对于在实际LifeCycleOwner事件返回之前分派的生命周期事件（如ON_STOP和ON_PAUSE）是必要的。</p><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><h4 id="添加观察者"><a href="#添加观察者" class="headerlink" title="添加观察者"></a>添加观察者</h4><p>首先我们从添加观察者说起，在Activity中，为什么可以获取到<code>Lifecycle</code>？</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Lifecycle <span class="title function_">getLifecycle</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> mLifecycleRegistry;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>跳转源码，可以看到在父类<code>ComponentActivity</code>中，实现 <code>LifecycleOwner</code> 接口， <code>getLifecycle </code>返回了 <code>mLifecycleRegistry</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ComponentActivity</span> <span class="keyword">extends</span> <span class="title class_">androidx</span>.core.app.ComponentActivity <span class="keyword">implements</span></span><br><span class="line">        <span class="title class_">ContextAware</span>,</span><br><span class="line">        LifecycleOwner,</span><br><span class="line">        ............</span><br><span class="line">        &#123;</span><br><span class="line">    ............</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">LifecycleRegistry</span> <span class="variable">mLifecycleRegistry</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LifecycleRegistry</span>(<span class="built_in">this</span>);</span><br><span class="line">    ............       </span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>在 ComponentActivity 创建的时候，同时创建了 LifecycleRegistry ，LifecycleRegistry 是Lifecycle 子类。</p><p>看到这里就可以理解为什么可以在Activity 里 添加观察者了。</p><p>接下来我们看下，添加观察者。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">LifecycleRegistry类   </span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addObserver</span><span class="params">(<span class="meta">@NonNull</span> LifecycleObserver observer)</span> &#123;</span><br><span class="line">        enforceMainThreadIfNeeded(<span class="string">&quot;addObserver&quot;</span>);</span><br><span class="line">        <span class="comment">//初始状态</span></span><br><span class="line">        <span class="type">State</span> <span class="variable">initialState</span> <span class="operator">=</span> mState == DESTROYED ? DESTROYED : INITIALIZED;</span><br><span class="line">        <span class="comment">//通过ObserverWithState将观察者和状态进行包装</span></span><br><span class="line">        <span class="type">ObserverWithState</span> <span class="variable">statefulObserver</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObserverWithState</span>(observer, initialState);</span><br><span class="line">        <span class="comment">//将包装类放到 FastSafeIterableMap&lt;LifecycleObserver, ObserverWithState&gt; 中 。</span></span><br><span class="line">        <span class="type">ObserverWithState</span> <span class="variable">previous</span> <span class="operator">=</span> mObserverMap.putIfAbsent(observer, statefulObserver);</span><br><span class="line">        ............</span><br><span class="line">        <span class="comment">//将状态进行对齐  </span></span><br><span class="line">        <span class="keyword">while</span> ((statefulObserver.mState.compareTo(targetState) &lt; <span class="number">0</span></span><br><span class="line">                &amp;&amp; mObserverMap.contains(observer))) &#123;</span><br><span class="line">            pushParentState(statefulObserver.mState);</span><br><span class="line">            <span class="keyword">final</span> <span class="type">Event</span> <span class="variable">event</span> <span class="operator">=</span> Event.upFrom(statefulObserver.mState);</span><br><span class="line">            <span class="keyword">if</span> (event == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;no event up from &quot;</span> +                statefulObserver.mState);</span><br><span class="line">            &#125;</span><br><span class="line">            statefulObserver.dispatchEvent(lifecycleOwner, event);</span><br><span class="line">            popParentState();         </span><br><span class="line">            targetState = calculateTargetState(observer);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!isReentrance) &#123;</span><br><span class="line">            <span class="comment">//同步状态 </span></span><br><span class="line">            sync();</span><br><span class="line">        &#125;</span><br><span class="line">        mAddingObserverCounter--;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>使用 <code>ObserverWithState</code>类 将观察者对象和状态进行包装，然后存储在 <code>FastSafeIterableMap</code> 中，这里先对<code>ObserverWithState</code>这个类有点印象， 在下文事件分发的时候，会再详细讲解。</p><p>FastSafeIterableMap 具有以下特性：</p><ul><li>支持键值对存储，用链表实现，模拟成Map的接口</li><li>支持在遍历的过程中删除任意元素，不会触发ConcurrentModifiedException</li><li>非线程安全</li></ul><p>注释中提到的将状态进行对齐，是在添加观察者的时候，可能是在任何状态的时候进行添加的，lifecycle需要将这个状态进行对齐。</p><p>例如我们在onResume的回调中添加了观察者，此时我们的观察者就收到依次收到 <code>onCreate</code>， <code>onStart</code>，<code>onResume</code>的回调。</p><h4 id="监听生命周期"><a href="#监听生命周期" class="headerlink" title="监听生命周期"></a>监听生命周期</h4><p>我们的观察者是怎样收到Activity 的生命周期的监听的呢？</p><p>这里我们看下父类<code>ComponentActivity</code>的onCreate方法中，有一个这样的操作</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">onCreate</span><span class="params">(<span class="meta">@Nullable</span> Bundle savedInstanceState)</span> &#123;</span><br><span class="line">    ............</span><br><span class="line">    ReportFragment.injectIfNeededIn(<span class="built_in">this</span>);</span><br><span class="line">    <span class="keyword">if</span> (mContentLayoutId != <span class="number">0</span>) &#123;</span><br><span class="line">        setContentView(mContentLayoutId);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">injectIfNeededIn</span><span class="params">(Activity activity)</span> &#123;</span><br><span class="line">    ........</span><br><span class="line">    android.app.<span class="type">FragmentManager</span> <span class="variable">manager</span> <span class="operator">=</span> activity.getFragmentManager();</span><br><span class="line">    <span class="keyword">if</span> (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == <span class="literal">null</span>) &#123;</span><br><span class="line">        manager.beginTransaction().add(<span class="keyword">new</span> <span class="title class_">ReportFragment</span>(), REPORT_FRAGMENT_TAG).commit();</span><br><span class="line">        <span class="comment">// Hopefully, we are the first to make a transaction.</span></span><br><span class="line">        manager.executePendingTransactions();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在 <code>ComponentActivity</code> 创建的时候，同时创建了<code>ReportFragment</code>，这个<code>Fragment</code>的作用就是用来分发生命周期状态的。</p><p>在<code>ReportFragment</code>的每个生命周期的回调中，都对应的调用了 <code>dispatch</code> 方法 ，来进行状态分发。</p><p>最后 <code>dispatch</code>的方法中，又调用了 <code>LifecycleRegistry</code> 的<code>handleLifecycleEvent</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleLifecycleEvent</span><span class="params">(<span class="meta">@NonNull</span> Lifecycle.Event event)</span> &#123;</span><br><span class="line">    enforceMainThreadIfNeeded(<span class="string">&quot;handleLifecycleEvent&quot;</span>);</span><br><span class="line">    moveToState(event.getTargetState());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们当前的状态是通过 <code>event.getTargetState()</code>来获取的</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> State <span class="title function_">getTargetState</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">switch</span> (<span class="built_in">this</span>) &#123;</span><br><span class="line">        <span class="keyword">case</span> ON_CREATE:</span><br><span class="line">        <span class="keyword">case</span> ON_STOP:</span><br><span class="line">            <span class="keyword">return</span> State.CREATED;</span><br><span class="line">        <span class="keyword">case</span> ON_START:</span><br><span class="line">        <span class="keyword">case</span> ON_PAUSE:</span><br><span class="line">            <span class="keyword">return</span> State.STARTED;</span><br><span class="line">        <span class="keyword">case</span> ON_RESUME:</span><br><span class="line">            <span class="keyword">return</span> State.RESUMED;</span><br><span class="line">        <span class="keyword">case</span> ON_DESTROY:</span><br><span class="line">            <span class="keyword">return</span> State.DESTROYED;</span><br><span class="line">        <span class="keyword">case</span> ON_ANY:</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="built_in">this</span> + <span class="string">&quot; has no target state&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里是维护了一个状态机，State 一共维护了 <code>DESTROYED</code> ， <code>INITIALIZED</code> ， <code>CREATED</code> ，<code>STARTED</code> ， <code>RESUMED</code> 五种状态</p><p>上图简要说明了一下 各个生命周期回调时，分别对应的状态，整个状态趋势 是分为 正在可见 和正在不可见。</p><p>通过状态机获取当前的状态之后，会将当前状态进行记录，然后会 执行 <code>sync()</code> 方法进行同步操作。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">sync</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">LifecycleOwner</span> <span class="variable">lifecycleOwner</span> <span class="operator">=</span> mLifecycleOwner.get();</span><br><span class="line">    ......</span><br><span class="line">    <span class="comment">//当 mObserverMap 中存储的最新的状态(刚插入的) 与 最老的状态(最先插入的) 一致，并且当前状态等于最新的状态，停止同步。 </span></span><br><span class="line">    <span class="keyword">while</span> (!isSynced()) &#123;</span><br><span class="line">        mNewEventOccurred = <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (mState.compareTo(mObserverMap.eldest().getValue().mState) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            backwardPass(lifecycleOwner);</span><br><span class="line">        &#125;</span><br><span class="line">        Map.Entry&lt;LifecycleObserver, ObserverWithState&gt; newest = mObserverMap.newest();</span><br><span class="line">        <span class="keyword">if</span> (!mNewEventOccurred &amp;&amp; newest != <span class="literal">null</span></span><br><span class="line">                &amp;&amp; mState.compareTo(newest.getValue().mState) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            forwardPass(lifecycleOwner);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    mNewEventOccurred = <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在这里会通过当前状态和存储的状态进行比较操作，判断当然流程 是 向正在可见发展 还是 正在向不可见发展，</p><p>例如当前执行的状态是<code>START</code>，与上个状态相比，如果上个状态是<code>CREATE</code>，相比结果就是&gt;0 ，说明是正在可见；如果上个状态是<code>RESUME</code> ，相比结果&lt;0 ，说明是正在不可见。</p><p><code>backwardPass</code> 和 <code>forwardPass</code> 主要有两点不同</p><p>1.在对<code>mObserverMap</code> 存储的状态进行遍历时，<code>backwardPass</code> 是以 栈的形式遍历，<code>forwardPass</code>是以队列的形式遍历。</p><p>2.对状态还原的时候，<code>backwardPass</code> 是不可见方向还原，也就是上图的粉色箭头方向，<code>forwardPass</code> 是以可见方向还原，也就是上图的 青绿色箭头方向。</p><p>最后是通过<code>ObserverWithState</code> 的 <code>dispatchEvent</code> 方法 进行了分发。</p><p>最后是怎样通知到我们对应的观察者对象的呢？</p><p><code>ObserverWithState</code> 在上文有提到，是对观察者对象和状态进行了一个包装，在这里我们来详细看一下。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">ObserverWithState</span> &#123;</span><br><span class="line">    State mState;</span><br><span class="line">    LifecycleEventObserver mLifecycleObserver;</span><br><span class="line"></span><br><span class="line">    ObserverWithState(LifecycleObserver observer, State initialState) &#123;</span><br><span class="line">        mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);</span><br><span class="line">        mState = initialState;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">dispatchEvent</span><span class="params">(LifecycleOwner owner, Event event)</span> &#123;</span><br><span class="line">        <span class="type">State</span> <span class="variable">newState</span> <span class="operator">=</span> event.getTargetState();</span><br><span class="line">        mState = min(mState, newState);</span><br><span class="line">        mLifecycleObserver.onStateChanged(owner, event);</span><br><span class="line">        mState = newState;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们先看包装的时候的操作，是执行了一个 <code>Lifecycling.lifecycleEventObserver(observer)</code> 操作。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">    <span class="keyword">static</span> LifecycleEventObserver <span class="title function_">lifecycleEventObserver</span><span class="params">(Object object)</span> &#123;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">isLifecycleEventObserver</span> <span class="operator">=</span> object <span class="keyword">instanceof</span> LifecycleEventObserver;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">isFullLifecycleObserver</span> <span class="operator">=</span> object <span class="keyword">instanceof</span> FullLifecycleObserver;</span><br><span class="line">        <span class="keyword">if</span> (isLifecycleEventObserver &amp;&amp; isFullLifecycleObserver) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">FullLifecycleObserverAdapter</span>((FullLifecycleObserver) object,</span><br><span class="line">                    (LifecycleEventObserver) object);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (isFullLifecycleObserver) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">FullLifecycleObserverAdapter</span>((FullLifecycleObserver) object, <span class="literal">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>是返回一个对应类型 的实例化对象 ，我们自定义的观察者，实现的是<code>DefaultLifecycleObserver</code> 接口，<code>DefaultLifecycleObserver</code>接口又继承了<code>FullLifecycleObserver</code> ，所以这里会新建一个 <code>FullLifecycleObserverAdapter</code> 对象，并返回，</p><p>再看 <code>ObserverWithState</code> 的 <code>dispatchEvent</code> 操作,调用了 <code>onStateChanged</code> 方法，然后就会调用到 <code>FullLifecycleObserverAdapter</code> 的 <code>onStateChanged</code> 里面</p><p>在这里面 就是进行接口回调，然后我们的观察者就可以接收到生命周期的回调了。</p><p>分析到这里，我们就可以将整个添加观察者，对应生命周期时间分发的过程全部联系起来了。</p><p><a href="https://zhuanlan.zhihu.com/p/484383470">参考：Lifecycle原理解析，人人都能看得懂！🔗</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;LifeCycle 组件与 Android 组件（如 Activity 或 Fragment）的生命周期事件相关，它包含三个主要的类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LifeCycle（生命周期）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LifeCy</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="Jetpack" scheme="https://adewu.github.io/tags/Jetpack/"/>
    
  </entry>
  
  <entry>
    <title>RecyclerView性能优化指南</title>
    <link href="https://adewu.github.io/2018/11/19/recyclerView_opt/"/>
    <id>https://adewu.github.io/2018/11/19/recyclerView_opt/</id>
    <published>2018-11-19T03:21:00.000Z</published>
    <updated>2024-02-11T12:50:24.210Z</updated>
    
    <content type="html"><![CDATA[<h3 id="数据处理与视图绑定分离"><a href="#数据处理与视图绑定分离" class="headerlink" title="数据处理与视图绑定分离"></a>数据处理与视图绑定分离</h3><p><strong>RecyclerView的<code>bindViewHolder</code>方法是在UI线程进行的，如果在该方法进行耗时操作，将会影响滑动的流畅性。</strong></p><p>优化前：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Task</span> &#123;</span><br><span class="line">    Date dateDue;</span><br><span class="line">    String title;</span><br><span class="line">    String description;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// getters and setters here</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyRecyclerView</span>.Adapter <span class="keyword">extends</span> <span class="title class_">RecyclerView</span>.Adapter &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="type">final</span> <span class="variable">TODAYS_DATE</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line">    <span class="keyword">static</span> <span class="type">final</span> <span class="variable">DATE_FORMAT</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleDateFormat</span>(<span class="string">&quot;MM dd, yyyy&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">onBindViewHolder</span><span class="params">(Task.ViewHolder tvh, <span class="type">int</span> position)</span> &#123;</span><br><span class="line">        <span class="type">Task</span> <span class="variable">task</span> <span class="operator">=</span> getItem(position);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (TODAYS_DATE.compareTo(task.dateDue) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            tvh.backgroundView.setColor(Color.GREEN);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            tvh.backgroundView.setColor(Color.RED);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">dueDateFormatted</span> <span class="operator">=</span> DATE_FORMAT.format(task.getDateDue());</span><br><span class="line">        tvh.dateTextView.setDate(dueDateFormatted);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的<code>onBindViewHolder</code>方法中进行了日期的比较和日期的格式化，这个是很耗时的，在<code>onBindViewHolder</code>方法中，应该只是将数据<code>set</code>到视图中，而不应进行业务的处理。</p><p>优化后：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TaskViewModel</span> &#123;</span><br><span class="line">    <span class="type">int</span> overdueColor;</span><br><span class="line">    String dateDue;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">onBindViewHolder</span><span class="params">(Task.ViewHolder tvh, <span class="type">int</span> position)</span> &#123;</span><br><span class="line">    <span class="type">TaskViewModel</span> <span class="variable">taskViewModel</span> <span class="operator">=</span> getItem(position);</span><br><span class="line">    tvh.backgroundView.setColor(taskViewModel.getOverdueColor());</span><br><span class="line">    tvh.dateTextView.setDate(taskViewModel.getDateDue());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="数据优化"><a href="#数据优化" class="headerlink" title="数据优化"></a>数据优化</h3><ol><li><strong>分页加载远端数据，对拉取的远端数据进行缓存，提高二次加载速度；</strong></li><li><strong>对于新增或删除数据通过DiffUtil，来进行局部数据刷新，而不是一味的全局刷新数据</strong></li></ol><p><code>DiffUtil</code>是support包下新增的一个工具类，用来判断新数据和旧数据的差别，从而进行局部刷新。</p><p>DiffUtil的使用，在原来调用<code>mAdapter.notifyDataSetChanged()</code>的地方：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// mAdapter.notifyDataSetChanged()</span></span><br><span class="line">DiffUtil.<span class="type">DiffResult</span> <span class="variable">diffResult</span> <span class="operator">=</span> DiffUtil.calculateDiff(<span class="keyword">new</span> <span class="title class_">DiffCallBack</span>(oldDatas, newDatas), <span class="literal">true</span>);</span><br><span class="line">diffResult.dispatchUpdatesTo(mAdapter);</span><br></pre></td></tr></table></figure><p>DiffUtil最终是调用Adapter的下面几个方法来进行局部刷新：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mAdapter.notifyItemRangeInserted(position, count);</span><br><span class="line">mAdapter.notifyItemRangeRemoved(position, count);</span><br><span class="line">mAdapter.notifyItemMoved(fromPosition, toPosition);</span><br><span class="line">mAdapter.notifyItemRangeChanged(position, count, payload);</span><br></pre></td></tr></table></figure><h3 id="布局优化"><a href="#布局优化" class="headerlink" title="布局优化"></a>布局优化</h3><h4 id="减少过度绘制"><a href="#减少过度绘制" class="headerlink" title="减少过度绘制"></a>减少过度绘制</h4><p><strong>减少布局层级，可以考虑使用自定义View来减少层级，或者更合理的设置布局来减少层级。</strong></p><p><strong>Note</strong>: 目前不推荐在RecyclerView中使用<code>ConstraintLayout</code>，在ConstraintLayout1.1.2版中，性能还是表现不佳，后续的版本可能这个问题就解决了，需要持续关注。</p><h4 id="减少xml文件inflate时间"><a href="#减少xml文件inflate时间" class="headerlink" title="减少xml文件inflate时间"></a>减少xml文件inflate时间</h4><p><strong>xml文件包括：layout、drawable的xml，xml文件inflate出ItemView是通过耗时的IO操作。可以使用代码去生成布局，即<code>new View()</code>的方式。这种方式是比较麻烦，但是在布局太过复杂，或对性能要求比较高的时候可以使用。</strong></p><h4 id="减少View对象的创建"><a href="#减少View对象的创建" class="headerlink" title="减少View对象的创建"></a>减少View对象的创建</h4><p><strong>一个稍微复杂的 Item 会包含大量的 View，而大量的 View 的创建也会消耗大量时间，所以要尽可能简化 ItemView；设计 ItemType 时，对多 ViewType 能够共用的部分尽量设计成自定义 View，减少 View 的构造和嵌套。</strong></p><h4 id="设置高度固定"><a href="#设置高度固定" class="headerlink" title="设置高度固定"></a>设置高度固定</h4><p><strong>如果item高度是固定的话，可以使用<code>RecyclerView.setHasFixedSize(true);</code>来避免requestLayout浪费资源。</strong></p><h4 id="共用RecycledViewPool"><a href="#共用RecycledViewPool" class="headerlink" title="共用RecycledViewPool"></a>共用RecycledViewPool</h4><p><strong>在嵌套RecyclerView中，如果子RecyclerView具有相同的adapter，那么可以设置<code>RecyclerView.setRecycledViewPool(pool)</code>来共用一个RecycledViewPool。</strong></p><p><strong>Note</strong>: 如果LayoutManager是LinearLayoutManager或其子类，需要手动开启这个特性：<code>layout.setRecycleChildrenOnDetach(true)</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">OuterAdapter</span> <span class="keyword">extends</span> <span class="title class_">RecyclerView</span>.Adapter&lt;OuterAdapter.ViewHolder&gt; &#123;</span><br><span class="line">    RecyclerView.<span class="type">RecycledViewPool</span> <span class="variable">mSharedPool</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RecyclerView</span>.RecycledViewPool();</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> OuterAdapter.ViewHolder <span class="title function_">onCreateViewHolder</span><span class="params">(ViewGroup parent, <span class="type">int</span> viewType)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">RecyclerView</span> <span class="variable">innerLLM</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RecyclerView</span>(inflater.getContext());</span><br><span class="line"></span><br><span class="line">        <span class="type">LinearLayoutManager</span> <span class="variable">innerLLM</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LinearLayoutManager</span>(parent.getContext(), LinearLayoutManager.HORIZONTAL);</span><br><span class="line">        innerLLM.setRecycleChildrenOnDetach(<span class="literal">true</span>);</span><br><span class="line">        innerRv.setLayoutManager(innerLLM);</span><br><span class="line">        innerRv.setRecycledViewPool(mSharedPool);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">OuterAdapter</span>.ViewHolder(innerRv);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="RecyclerView数据预取"><a href="#RecyclerView数据预取" class="headerlink" title="RecyclerView数据预取"></a>RecyclerView数据预取</h3><p><strong>RecyclerView25.1.0及以上版本增加了<code>Prefetch</code>功能。</strong><br><strong>用于嵌套RecyclerView获取最佳性能。</strong></p><p><strong>Note</strong>: 只适合横向嵌套</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在嵌套内部的LayoutManager中调用LinearLayoutManger的设置方法</span></span><br><span class="line"><span class="comment">// num的取值：如果列表刚刚展示4个半item，则设置为5</span></span><br><span class="line">innerLLM.setInitialItemsPrefetchCount(num);</span><br></pre></td></tr></table></figure><h3 id="加大RecyclerView的缓存"><a href="#加大RecyclerView的缓存" class="headerlink" title="加大RecyclerView的缓存"></a>加大RecyclerView的缓存</h3><p><strong>用空间换时间，来提高滚动的流畅性。</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">recyclerView.setItemViewCacheSize(<span class="number">20</span>);</span><br><span class="line">recyclerView.setDrawingCacheEnabled(<span class="literal">true</span>);</span><br><span class="line">recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);</span><br></pre></td></tr></table></figure><h3 id="增加RecyclerView预留的额外空间"><a href="#增加RecyclerView预留的额外空间" class="headerlink" title="增加RecyclerView预留的额外空间"></a>增加RecyclerView预留的额外空间</h3><p><strong>额外空间：显示范围之外，应该额外缓存的空间</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">LinearLayoutManager</span>(<span class="built_in">this</span>) &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="type">int</span> <span class="title function_">getExtraLayoutSpace</span><span class="params">(RecyclerView.State state)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="减少ItemView监听器的创建"><a href="#减少ItemView监听器的创建" class="headerlink" title="减少ItemView监听器的创建"></a>减少ItemView监听器的创建</h3><p><strong>对ItemView设置监听器，不要对每个item都创建一个监听器，而应该共用一个XxListener，然后根据<code>ID</code>来进行不同的操作，优化了对象的频繁创建带来的资源消耗。</strong></p><h3 id="优化滑动操作"><a href="#优化滑动操作" class="headerlink" title="优化滑动操作"></a>优化滑动操作</h3><p><strong>设置<code>RecyclerView.addOnScrollListener();</code>来在滑动过程中停止加载的操作。</strong></p><h3 id="处理刷新闪烁"><a href="#处理刷新闪烁" class="headerlink" title="处理刷新闪烁"></a>处理刷新闪烁</h3><p><strong>调用notifyDataSetChange时，适配器不知道整个数据集中的那些内容以及存在，再重新匹配ViewHolder时会花生闪烁。</strong><br><strong>设置adapter.setHasStableIds(true)，并重写getItemId()来给每个Item一个唯一的ID</strong></p><h3 id="回收资源"><a href="#回收资源" class="headerlink" title="回收资源"></a>回收资源</h3><p><strong>通过重写<code>RecyclerView.onViewRecycled(holder)</code>来回收资源。</strong></p><p><a href="https://zhuanlan.zhihu.com/p/49338922">原文链接🔗</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;数据处理与视图绑定分离&quot;&gt;&lt;a href=&quot;#数据处理与视图绑定分离&quot; class=&quot;headerlink&quot; title=&quot;数据处理与视图绑定分离&quot;&gt;&lt;/a&gt;数据处理与视图绑定分离&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;RecyclerView的&lt;code&gt;bindVie</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="RecyclerView" scheme="https://adewu.github.io/tags/RecyclerView/"/>
    
  </entry>
  
  <entry>
    <title>初识Flutter</title>
    <link href="https://adewu.github.io/2018/09/27/flutter_good_bad_ugly/"/>
    <id>https://adewu.github.io/2018/09/27/flutter_good_bad_ugly/</id>
    <published>2018-09-27T11:21:00.000Z</published>
    <updated>2024-02-11T12:48:47.089Z</updated>
    
    <content type="html"><![CDATA[<p>在2017年，Google在Google IO开发者大会上公开发布了早期版本的跨平台移动应用技术Flutter。快进到今年的大会，官宣Flutter已经准备好投入生产环境，在九月底发布了Release Preview 2。这是其中一些亮点：</p><ol><li>这个最新版本的主题是完美的iOS应用程序（之前主要关注Android的Material Design）。</li><li>减少应用程序的包大小，现在一个最小的Android应用程序在发布模式下只有4.7MB。</li><li>Flutter自从开源以来，一直是GitHub上活跃排名前50的仓库之一。 </li><li>大厂正在使用它，比如阿里（Android，iOS）、腾讯（Android，iOS）和Google广告（Android，iOS）。还有一个关于阿里巴巴如何使用Flutter构建闲鱼应用程序（Android，iOS）的视频，目前在中国有超过5000万客户在使用。</li><li>这张图显示了Flutter在Stack Overflow上得到了很多关注。</li></ol><p><img src="/../imgs/flutter_trend_2018.webp" alt="alt text" title="follows"></p><h3 id="Flutter是什么"><a href="#Flutter是什么" class="headerlink" title="Flutter是什么"></a>Flutter是什么</h3><ol><li><p>Flutter是由Google开发的开源软件开发工具包（SDK），用于快速构建iOS和Android应用程序，并开源大部分代码。它与Android和iOS SDK配合使用，这也意味着你仍然需要一台macOS机器来构建iOS应用程序（就像你为React Native和Xamarin所做的那样）。对于我来说，安装Android版本非常顺利——我只需按照网站上的说明操作，并使用’flutter doctor’命令，但我最初在iOS设置方面遇到了一些问题。</p></li><li><p>它使用了由Google开发的Dart语言。是的，又是一个需要学习的语言，但别担心，如果你熟悉Java、JS、Kotlin、Swift或C#，那么它非常容易上手。</p></li><li><p>应用程序是预先编译为本地ARM代码的，而不是像React Native那样在运行时。这提供了更好的性能，因为没有中间的JS桥梁来解析和执行代码。但这也意味着没有通过在运行时下载新的JS代码束来进行OTA更新的选项。</p></li><li><p>它与React Native类似，也是基于“单向数据流”架构，或者说是响应式编程。简而言之，应用程序通过改变变量&#x2F;属性（或者更通用地说，屏幕或视图的“状态”）来响应用户输入，然后根据新的状态重新呈现UI。函数不直接改变UI（按钮的颜色、标签的文本、列表的内容等）。</p></li><li><p>与React Native类似，Flutter也支持热重载。只需在代码编辑器中进行更改，保存，Android模拟器或iOS模拟器上的UI就会刷新。这是如此方便和快速，一旦尝试过就很难回去，它弥补了UI是以编程方式创建的，因此没有可视化编辑器的缺陷。</p></li><li><p>Flutter可以通过第三方插件进行扩展，这些插件可以添加新的自定义UI组件或封装平台特定的功能，而这些功能不是内置类已经涵盖的（例如：视频&#x2F;音频、货币化、存储、相机、增强现实、机器学习等）。这是我发现的最好的插件集合。有很多插件，但可能不是你所需要的全部。</p></li><li><p>与前面一点相关，Flutter通过在检查Platform.isIOS和Platform.isAndroid之后执行不同的代码（如果在你的.dart文件中，你想要实例化不同的UI小部件，或者逻辑不同），或者编写你自己的原生插件（如果你真的需要封装Flutter尚未提供的平台特定功能），来相对容易地编写平台特定的代码。</p></li><li><p>与第7点相关，对于典型的应用程序来说，性能不应该是一个问题（至少在发布模式下——调试模式明显较慢，因为它使用虚拟机来运行Dart代码），因为UI是由一个高效的底层C++库编写的，其他功能映射到它们的原生对应项。但是，必须注意的是：通过最小化重绘次数，并且仅重绘实际上依赖于已更改状态的部分。</p></li><li><p>你可以使用任何文本编辑器和flutter命令来编写和构建应用程序，但推荐的方法是使用支持Flutter插件的编辑器之一，即Android Studio、VS Code或Intelli J。这可以为你提供智能感知、自动完成、一些调试工具，免去了你需要使用命令行编译&#x2F;运行应用程序的烦恼。</p></li></ol><h3 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h3><ol><li><p>Flutter的UI绘制独立于平台特定原生组件的封装具有优缺点。优点是，如果某些内容在你的测试iPhone上以iOS 12的某种方式呈现，例如，它应该在任何其他iOS版本以及任何Android手机上以完全相同的方式呈现。而对于React Native或Xamarin，UI组件具有一些仅在一个平台或另一个平台上支持的属性，或者它们可能受支持，但在后台将其翻译为稍微不同的方式到它们的原生对应项。这意味着你要么需要在许多设备和操作系统版本上进行测试（并可能编写平台特定的代码来修复某些情况），要么只是知道它可能会在某些用户那里看起来不正常（或至少不同）。如果你在特定的OS版本上使用了不支持的属性或功能，你的应用程序甚至可能会崩溃。使用Flutter，你将更加安全（至少对于应用程序的UI部分来说）。你仍然应该在多台设备上检查应用程序，特别是如果你使用第三方插件，这些插件将映射到底层平台特定的原生组件。如果你使用诸如音频&#x2F;视频、推送通知、应用内计费等功能，情况就会是这样。这个方法的负面影响将在文章的下一节中进行讨论。</p></li><li><p>热重载太有用了，这是开发者的梦想成真：在编辑器中按Ctrl+S，模拟器上的应用程序就在一秒钟内重新加载！告别了无尽的构建&#x2F;等待&#x2F;运行&#x2F;等待&#x2F;测试&#x2F;重新开始的过程。实际上，当你改变资源和插件，改变导航、状态初始化或逻辑时，你仍然需要重新构建，但大多数UI更改都会立即应用到正在运行的应用程序中。对于UI密集型的应用程序，这是你将花费大部分时间的地方。</p></li><li><p>我喜欢小型可重用组件的整体原则，它们对“状态”变化做出反应，这也是React和React Native的核心思想之一。当然，也可以在纯iOS和Android开发中开发响应式应用程序，但使用Flutter（和RN）会更容易和更自然。这是因为它是技术的核心，而不是由第三方库提供并以几十种不同的方式实现的功能。</p></li><li><p>Dart语言简单但功能强大且完整，可以与Swift、Kotlin或Java相媲美。使用async&#x2F;await&#x2F;Future进行异步编程非常简单。</p></li><li><p>Flutter和Dart内置支持逻辑的单元测试和UI&#x2F;交互的部件测试。例如，你可以发送轻触和滚动手势，在小部件树中找到子小部件，读取文本，并验证小部件属性的值是否正确。官方文档清楚地介绍了可用的内容。</p></li><li><p>我喜欢应用程序UI的每个方面都有主题化支持。创建我应用程序的浅色和深色主题中最困难的部分实际上是选择正确的颜色（我只创建了两个，但可以用相同的方法创建10个）。在代码方面，只需要几行代码（基本上设置根MaterialApp对象的主题属性）。</p></li></ol><h3 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h3><p>我必须说，我并没有找到任何值得列在“缺点”或“不足”部分的东西，但以下是一些在某种程度上并不那么好的事情，至少从某些角度来看：</p><ol><li><p>正如已经多次提到的，Flutter以自己的自定义方式绘制UI，它不创建原生组件。它在复制Android的Material Design和iOS特定组件方面做得非常好，但在底层仍然不是原生的。这有一些影响，比如：<br>（A）如果iOS 13改变了分段控件或UISwitch的呈现方式，那么你使用CupertinoSegmentedControl或CupertinoSwitch的Flutter应用程序在Flutter更新并重建之前将保持旧的外观。可以争论说很多用户不会在乎（我的大多数非技术朋友不会在乎，甚至不会注意到，例如，他们只关心应用程序看起来是否足够漂亮，而不是它是否与OS的纯净外观和感觉完全一致），但如果你是一个纯粹主义者，这可能是一个决定性因素。<br>（B）如果你计划在现有应用程序的某些功能使用Flutter，你可能会看到原生部分和Flutter部分之间的差异。同样，这可能会让你的用户感到困扰或不困扰。对于纯Flutter的应用来说，这个问题就小得多了。<br>（C）为了尽可能地简化开发过程，并假设你的用户不关心应用程序的原生外观，你可以只使用MaterialApp（它使用Material Design组件）并将其编译为Android和iOS。它将正常工作，尽管外观不是原生的，事实上这就是我为我的应用程序所做的。如果你真的关心这一点，决定在Android上使用MaterialApp，而在iOS上使用CupertinoApp，你将为UI大部分，如果不是全部的代码进行复制（这可能是你应用程序的一个相当大的部分），并且会使架构更加复杂。请认真考虑这一点，并决定是否值得这样做。</p></li><li><p>因为Flutter是更新的，并且拥有规模更小的社区，它远不及你可以在React Native甚至Xamarin中找到的插件丰富，但这就是目前的情况。选择有限，许多插件已经过时，不再维护，甚至可能不再与当前的Dart&#x2F;Flutter版本兼容。一些组件（特别是非UI组件，映射平台特定功能）仅适用于iOS或Android，而不是两者（通常它们支持Android，因为Android开发者目前比iOS开发者更热衷于Flutter，因为Flutter来自Google）。然而，填补空白并为缺失的平台编写平台特定的代码仍然比从头开始好，如果Flutter继续变得越来越受欢迎，情况肯定会有所改善。</p></li><li><p>当出现布局错误（或其他低级错误）时，你得到的错误屏幕或日志可能会非常令人困惑和晦涩，因为它指向框架的某行代码，这可能是在你直接交互的许多抽象层之下。在原生iOS和Android上，错误通常更容易理解，如果不行，你通常可以将完整的错误复制粘贴到Google上，完全可以得到更多有用的链接来了解更多信息。由于社区仍然相对较小，Flutter的情况并不是如此。</p></li><li><p>在相同的.dart文件中（你的屏幕代码的位置）创建UI是简单直接的。这也意味着没有太多的分离。我更喜欢在单独的文件中使用标记代码创建UI（类似于在原生Android应用程序中所做的），而不是通过编程方式创建UI。</p></li><li><p>在Android上，绝大多数开发人员使用Clean Architecture和MVP（模型-视图-控制器）。在iOS上，它可以是MVC、MVVM（模型-视图-视图模型）或Viper。对于Flutter（以及React Native），感觉就像一切仍在定义中，没有“标准”或“几乎普遍接受”的架构方法。所有文章都显示简单的示例，这是正常的，因为他们仍然需要吸引人们，然后再谈论更高级的方面。然而，如果你计划将Flutter用于一个相当大的项目，那么最好有一个清晰的想法来构建它，这样它就可以随着应用程序的增长而增长，并且易于维护。再次强调，我绝对不是说Flutter不允许你构建具有清晰和可维护架构的应用程序，但只是说会涉及一些试错、实验和学习，因为它不像我们在原生iOS&#x2F;Android应用程序中习惯的那样成熟和广泛使用。</p></li></ol><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>Flutter有很大的潜力，很容易入门并着手开发，并且有许多优秀的设计原则和思想。但是社区仍然很小，在跨平台插件方面还有一些缺失，或者在最好的情况下选择不多。此外，你必须接受这样一个事实，即你不会拥有一个100%原生外观的UI，并且如果你想要至少尽可能接近iOS和Android的UI外观，你的代码和结构将变得更加复杂。</p><p>就我个人而言，我认为这是一种非常有用的技术（已经可以使用了），适用于以下情况：</p><ul><li>你必须尽快地触及尽可能广泛的用户群 — 比如一家从零开始的初创公司，希望为两个平台发布。看看有没有一个或两个平台有动力，然后再投资更多，打磨东西等等。你可以把这看作是一个非常先进的原型，稍后可以进一步打磨，仍然保持在Flutter上，或者用一个本地版本和一个专门的团队替换。</li><li>UI本来就不是最大的关注点 — 例如企业&#x2F;B2B应用程序，你希望有一款员工&#x2F;客户可以使用任何类型的设备的业务应用程序，但并不太关心它是否与OS生态系统中的其他一切保持一致。这就是Groupon为其面向商户的应用程序所做的（但不是针对最终用户）。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在2017年，Google在Google IO开发者大会上公开发布了早期版本的跨平台移动应用技术Flutter。快进到今年的大会，官宣Flutter已经准备好投入生产环境，在九月底发布了Release Preview 2。这是其中一些亮点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;这个最</summary>
      
    
    
    
    <category term="Flutter" scheme="https://adewu.github.io/categories/Flutter/"/>
    
    
    <category term="Flutter" scheme="https://adewu.github.io/tags/Flutter/"/>
    
  </entry>
  
  <entry>
    <title>Java Vs Kotlin</title>
    <link href="https://adewu.github.io/2018/09/15/kotlin_vs_java/"/>
    <id>https://adewu.github.io/2018/09/15/kotlin_vs_java/</id>
    <published>2018-09-15T03:21:00.000Z</published>
    <updated>2024-02-11T12:48:20.731Z</updated>
    
    <content type="html"><![CDATA[<h3 id="什么是-Kotlin？"><a href="#什么是-Kotlin？" class="headerlink" title="什么是 Kotlin？"></a>什么是 Kotlin？</h3><p>引用官方文档的说法，Kotlin 是一种开源的静态类型编程语言，目标是 JVM、Android、JavaScript 和 Native。它由 JetBrains 开发。它与 Java 完全兼容和可互操作。</p><h3 id="Kotlin-应用场景？"><a href="#Kotlin-应用场景？" class="headerlink" title="Kotlin 应用场景？"></a>Kotlin 应用场景？</h3><p>Kotlin 是一种可用于任何类型开发的编程语言——服务器端、客户端或 Web。</p><h3 id="Kotlin-和-Java-的区别"><a href="#Kotlin-和-Java-的区别" class="headerlink" title="Kotlin 和 Java 的区别"></a>Kotlin 和 Java 的区别</h3><table><thead><tr><th align="center">特征</th><th align="center">Java</th><th align="center">Kotlin</th></tr></thead><tbody><tr><td align="center">异常检查</td><td align="center">可用</td><td align="center">不可用</td></tr><tr><td align="center">代码简洁性</td><td align="center">不能被标记为简洁</td><td align="center">优于 Java</td></tr><tr><td align="center">协程</td><td align="center">不可用</td><td align="center">可用</td></tr><tr><td align="center">数据类</td><td align="center">需要编写大量样板代码</td><td align="center">只需在类定义中添加 data 关键字</td></tr><tr><td align="center">扩展函数</td><td align="center">不可用</td><td align="center">可用</td></tr><tr><td align="center">高阶函数和 Lambda</td><td align="center">高阶函数是使用可调用项实现的，Lambda 表达式在 Java 8 中引入作为预构建功能之一出现</td><td align="center">作为预先构建的功能之一</td></tr><tr><td align="center">隐式扩展转换</td><td align="center">可用</td><td align="center">不可用</td></tr><tr><td align="center">内联函数</td><td align="center">不可用</td><td align="center">可用</td></tr><tr><td align="center">委托的原生支持</td><td align="center">不可用</td><td align="center">可用</td></tr><tr><td align="center">非私有字段</td><td align="center">可用</td><td align="center">不可用</td></tr><tr><td align="center">空指针异常</td><td align="center">可用</td><td align="center">不可用</td></tr><tr><td align="center">原始类型</td><td align="center">原始类型的变量不是对象</td><td align="center">原始类型的变量是对象</td></tr><tr><td align="center">智能转换</td><td align="center">不可用</td><td align="center">可用</td></tr><tr><td align="center">静态成员</td><td align="center">可用</td><td align="center">不可用</td></tr><tr><td align="center">构造函数支持</td><td align="center">不能有次要构造函数。尽管可以有多个构造函数（构造函数重载）</td><td align="center">可以有一个或多个次要构造函数</td></tr><tr><td align="center">三元运算符</td><td align="center">可用</td><td align="center">不可用</td></tr><tr><td align="center">通配符类型</td><td align="center">可用</td><td align="center">不可用，具有声明点方差和类型投影作为替代方案</td></tr></tbody></table><h3 id="Kotlin-是否比-Java-更好？"><a href="#Kotlin-是否比-Java-更好？" class="headerlink" title="Kotlin 是否比 Java 更好？"></a>Kotlin 是否比 Java 更好？</h3><p>Java 已经存在了约 20 年，是 Android 自身构建的语言，并且在 Android 开发人员中长期以来备受推崇这一事实，Java 依然具有其位置，这是很难与之竞争的。那么，你应该选择学习哪种语言呢？</p><p>答案实际上取决于情况。在本文的后部分，我将详细讨论每种语言的优缺点，这样你就可以自行决定学习哪种语言。但是现在，为了简化问题，我想说一下：任何从事 Android 开发的人都必须具备 Java 的基本知识，即使你希望以后专攻 Kotlin 或其他语言。因此，如果你已经是一名 Java 程序员，现在想开始开发 Android 应用程序，可以从 Kotlin 开始。但如果你是一个完全的初学者，刚刚开始涉足编程的世界，那么从 Java 开始。Java 是 Android 开发的基石，因此，它可以帮助你学习编码规范，打好基础。</p><p>现在，让我们详细讨论 Java 和 Kotlin，看看哪一个更适合你：</p><h3 id="Java-的优劣势"><a href="#Java-的优劣势" class="headerlink" title="Java 的优劣势"></a>Java 的优劣势</h3><p>Java 是由 Sun Microsystems 开发的面向对象编程语言，现在由 Oracle 拥有。作为一种如此古老的语言，Java 不仅仅用于开发 Android 应用程序。让我们讨论一下 Java 的优缺点。</p><h4 id="优势"><a href="#优势" class="headerlink" title="优势"></a>优势</h4><ul><li>易于学习和理解。</li><li>适用于本地和跨平台应用程序。</li><li>由于 Android 本身是基于 Java 构建的，因此有许多 Java 库可供使用。</li><li>此外，Java 具有广泛的开源生态系统。</li><li>即使与 Kotlin 应用程序相比，Java 应用程序也更轻、更紧凑，从而提供更快的应用体验。</li><li>Java 生成速度更快，让你在更短的时间内编写更多的代码。</li><li>借助 Gradle 的加速组装，组装大型项目在 Java 中变得更容易。</li></ul><h4 id="劣势"><a href="#劣势" class="headerlink" title="劣势"></a>劣势</h4><ul><li>Java 是一种重类型的语言，这意味着你需要编写更多的代码，增加了错误和 bug 的机会。</li><li>由于固有限制，Java 在 Android API 设计方面遇到一些问题。</li><li>与其他语言相比，Java 需要更多的内存，也更慢。</li></ul><h3 id="Kotlin-没有的-Java-功能"><a href="#Kotlin-没有的-Java-功能" class="headerlink" title="Kotlin 没有的 Java 功能"></a>Kotlin 没有的 Java 功能</h3><h4 id="异常检查"><a href="#异常检查" class="headerlink" title="异常检查"></a>异常检查</h4><p>喜欢构建具有错误恢复的健壮代码的开发者，仍然非常青睐 Java 的检查异常功能。Kotlin 为了增加代码的简洁性而放弃了这些功能，但仍然受到许多开发者的喜爱。</p><h4 id="不是类的原始类型"><a href="#不是类的原始类型" class="headerlink" title="不是类的原始类型"></a>不是类的原始类型</h4><p>Char、double、float 和 byte 是 8 种原始类型之一，它们的唯一目的是包含某种类型的纯粹、简单的值。它们预定义了许多操作，在 Java 中，它们不是从类或结构中实例化的对象。</p><h4 id="静态成员"><a href="#静态成员" class="headerlink" title="静态成员"></a>静态成员</h4><p>尽管 Kotlin 没有静态成员的功能，但 Java 创建了静态成员的一个实例，该实例在类的所有实例之间共享。关键字 static 反映了特定成员所属的类型本身，而不是实例。</p><h4 id="通配符类型"><a href="#通配符类型" class="headerlink" title="通配符类型"></a>通配符类型</h4><p>通配符类型有多种用途，包括作为字段、参数或局部变量的类型。尽管 Kotlin 不提供通配符类型，但 Java 允许开发者使用此类型系统来扩展功能。</p><h4 id="三元运算符-a-b-c"><a href="#三元运算符-a-b-c" class="headerlink" title="三元运算符 a?b:c"></a>三元运算符 a?b:c</h4><p>与 Kotlin 不同，Java 具有一个三元运算符，它像一个基本的 if 语句，用于评估条件的 true 或 false。</p><h3 id="Kotlin的优劣势"><a href="#Kotlin的优劣势" class="headerlink" title="Kotlin的优劣势"></a>Kotlin的优劣势</h3><p>Kotlin 在 Android 开发需要一种更现代的语言以增加 Java 的特性并帮助移动开发时出现。它是一种基于 Java 虚拟机（JVM）的开源、静态类型语言。Kotlin 的优势在于你可以将其编译成 JavaScript，并将其与 Java 进行互操作。这使开发者不仅可以轻松地将旧的 Java 应用程序更新为 Kotlin，还可以在 Kotlin 中继续他们的旧工作。</p><h4 id="优势-1"><a href="#优势-1" class="headerlink" title="优势"></a>优势</h4><p>Kotlin 编码速度确实更快，因此开发者非常喜爱。如果在 Java 中需要 50 行代码，那么在 Kotlin 中只需要 1-2 行。这也意味着错误和 bug 更少。</p><ul><li>Kotlin 有助于构建清晰的 API。</li><li>由于 Java 字节码，你可以在 Kotlin 中使用 Java 库和框架，使从 Java 到 Kotlin 的过渡变得无缝。</li><li>Kotlin 的类型系统中有一个非常需要的 null，而 Java 中缺少这一点。Android 使用 null 表示值的缺失，而 Kotlin 允许你使用 null，极大地减轻了这一痛点。</li><li>对于开发者来说，Anko 库对项目有很大帮助，Github 上也有非常活跃且广泛的项目集合。</li></ul><h4 id="劣势-1"><a href="#劣势-1" class="headerlink" title="劣势"></a>劣势</h4><ul><li>学习 Kotlin 的曲线确实陡峭。尽管其高度简洁的语法是一个很大的优势，但需要一些前期学习。</li><li>在大多数情况下，Kotlin 的编译速度比 Java 慢，尽管在某些情况下它确实胜过了 Java。</li><li>Kotlin 社区仍然年轻，学习资源有限，因此找到问题的答案可能有些困难。然而，随着其日益普及，资源和社区将会扩大。</li><li>与 Java 相比，一些 Android Studio 的功能，如自动完成和编译，在 Kotlin 中运行速度较慢。</li></ul><h3 id="Kotlin-比-Java-多的功能"><a href="#Kotlin-比-Java-多的功能" class="headerlink" title="Kotlin 比 Java 多的功能"></a>Kotlin 比 Java 多的功能</h3><p>Kotlin 已经成为了 Android 开发者的新语言，为他们提供了更多的功能。</p><h4 id="Lambda-表达式和高阶函数"><a href="#Lambda-表达式和高阶函数" class="headerlink" title="Lambda 表达式和高阶函数"></a>Lambda 表达式和高阶函数</h4><p>Kotlin 编程语言提供了可以存储在数据结构和变量中的一级函数，因此函数可以以其他非函数值的所有方式进行操作。</p><p>Kotlin 作为静态类型语言，利用了各种函数类型，包括 Lambda 表达式等专门的语言构造。</p><h4 id="扩展函数"><a href="#扩展函数" class="headerlink" title="扩展函数"></a>扩展函数</h4><p>Kotlin 的扩展函数允许将对 View 的引用导入到 activity 文件中，从而允许将该视图作为活动的一部分进行操作。在 Kotlin 中创建扩展函数就像将要扩展的类的名称前缀与正在创建的函数的名称相同。</p><h4 id="空安全"><a href="#空安全" class="headerlink" title="空安全"></a>空安全</h4><p>开发者在 Java 中面临的最大问题之一就是 NullPointerExceptions。Kotlin 为 Android 开发者解决了这个令人恼火的问题，通过使所有类型默认为非空来实现。</p><h4 id="智能转换"><a href="#智能转换" class="headerlink" title="智能转换"></a>智能转换</h4><p>与 Java 不同，Kotlin 允许开发者自动处理冗余转换，无需检查类型或在语句内进行转换。只要转换已经使用 ‘is 操作符’ 进行了检查，Kotlin 编译器就会跟踪 ‘if 表达式’ 内部的条件。</p><h4 id="字符串模板"><a href="#字符串模板" class="headerlink" title="字符串模板"></a>字符串模板</h4><p>Kotlin 中的字符串模板允许开发者直接将动态值赋给字符串，并使用模板轻松打印 Kotlin 中的语句。</p><h4 id="主构造函数"><a href="#主构造函数" class="headerlink" title="主构造函数"></a>主构造函数</h4><p>Kotlin 中的主构造函数用于初始化类。它可以在类头级别声明，从而可以自动初始化变量。</p><h4 id="委托"><a href="#委托" class="headerlink" title="委托"></a>委托</h4><p>Kotlin 支持委托模式，无需样板代码。这为实现继承提供了一个有效的替代方案。派生类可以将其所有公共成员委托给指定的对象，从而轻松实现接口。</p><h4 id="变量和属性类型的类型推断"><a href="#变量和属性类型的类型推断" class="headerlink" title="变量和属性类型的类型推断"></a>变量和属性类型的类型推断</h4><p>Kotlin 作为一种强类型语言，支持类型推断。因此，你不需要显式地定义变量的数据类型，因为 Kotlin 编译器会自动为你完成。</p><h4 id="单例"><a href="#单例" class="headerlink" title="单例"></a>单例</h4><p>在 Kotlin 编程语言中，单例模式用作静态字段的替代品，可以通过简单地声明该对象来创建。单例模式确保只创建一个实例，并且它将作为单一访问点，从而确保线程安全。</p><h4 id="声明站点的差异和类型投影"><a href="#声明站点的差异和类型投影" class="headerlink" title="声明站点的差异和类型投影"></a>声明站点的差异和类型投影</h4><p>虽然 Java 具有通配符类型，但 Kotlin 具有在类声明上指定变异注释的能力。称为声明站点变异，这是一个方便的选项，因为这些注释适用于类在其中使用的所有位置。</p><p>因此，假设你想使用一个泛型类而不知道类型参数，类型投影会为你解决这个问题。</p><h4 id="范围表达式"><a href="#范围表达式" class="headerlink" title="范围表达式"></a>范围表达式</h4><p>在 Kotlin 中，你可以使用 rangeTo() 和 downTo() 函数为任何可比较类型创建范围，用于运算符。</p><h4 id="操作符重载"><a href="#操作符重载" class="headerlink" title="操作符重载"></a>操作符重载</h4><p>Kotlin 允许 Android 开发者为其类型提供预定义一组操作符的实现。这些运算符具有固定的符号表示和优先级。</p><h4 id="伴生对象"><a href="#伴生对象" class="headerlink" title="伴生对象"></a>伴生对象</h4><p>如果代码需要将函数或属性与类本身而不是其实例联系起来，可以将其声明在伴生对象中。伴生对象是一个单例，可以直接通过包含类的名称访问。</p><h4 id="数据类"><a href="#数据类" class="headerlink" title="数据类"></a>数据类</h4><p>数据类是仅包含状态且不执行任何操作的类。使用它们的最大优势是它们生成大量的自动生成代码。</p><h4 id="用于只读和可变集合的分离接口"><a href="#用于只读和可变集合的分离接口" class="headerlink" title="用于只读和可变集合的分离接口"></a>用于只读和可变集合的分离接口</h4><p>与 Java 不同，Kotlin 具有用于处理集合的单独接口。普通集合接口处理访问数据，而 MutableCollection 接口处理修改数据。</p><h4 id="协程"><a href="#协程" class="headerlink" title="协程"></a>协程</h4><p>Kotlin 协程是一种设计模式，你可以在 Android 上使用它来简化异步执行的代码。它有助于管理可能阻塞主线程的长时间运行的任务，使你的应用程序不会响应。</p><h3 id="Kotlin-的未来"><a href="#Kotlin-的未来" class="headerlink" title="Kotlin 的未来"></a>Kotlin 的未来</h3><p>对于 Android 开发者来说，Kotlin 的优势不容否认，这是未来开发的方向。许多企业正在将 Kotlin 作为其官方 Android 编程语言。Kotlin 作为新的官方语言的未来肯定是光明的。</p><p>公平地说，你的团队中可能已经有人熟悉 Java。一次性将整个团队转换为 Kotlin 可能会有点冲击，因此重要的是要慢慢进行过渡，逐个进行转换。话虽如此，由于其现代特性，Kotlin 将来肯定会被广泛采用，因此现在学习并开始使用 Kotlin 进行开发是个好主意。尽管 Java 将继续是一个长时间的热门语言，并且不太可能被完全取代。所以请花点时间，稳健地进行过渡。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;什么是-Kotlin？&quot;&gt;&lt;a href=&quot;#什么是-Kotlin？&quot; class=&quot;headerlink&quot; title=&quot;什么是 Kotlin？&quot;&gt;&lt;/a&gt;什么是 Kotlin？&lt;/h3&gt;&lt;p&gt;引用官方文档的说法，Kotlin 是一种开源的静态类型编程语言，目标</summary>
      
    
    
    
    <category term="Kotlin" scheme="https://adewu.github.io/categories/Kotlin/"/>
    
    
    <category term="Kotlin" scheme="https://adewu.github.io/tags/Kotlin/"/>
    
  </entry>
  
  <entry>
    <title>Android Things是否会成为物联网领域的重要参与者？【译】</title>
    <link href="https://adewu.github.io/2018/05/12/android_things/"/>
    <id>https://adewu.github.io/2018/05/12/android_things/</id>
    <published>2018-05-12T10:21:00.000Z</published>
    <updated>2024-02-11T12:49:09.145Z</updated>
    
    <content type="html"><![CDATA[<p>随着越来越多的企业看到Android的增长潜力，他们也在越来越多地进行Android移动应用程序开发。Android希望通过发布新功能和更新，如Android Things，继续利用这种增长势头。曾经的Brillo现在成为了Android Things，而Android Things最终达到了1.0版本。谷歌宣布，其物联网设备平台已经从开发者预览版中毕业，这是在SDK（软件开发工具包）下载量超过10万次之后的这周。</p><p>我曾认为谷歌准备建立自己的智能家居中心，但现在似乎这种可能性非常小。为什么呢？因为Android Things非常适合这样做，但它适用于使用特定芯片和板卡的任何硬件制造商。例如，谷歌的Android Things现在支持NXP i.MX8M、Qualcomm SDA212、Qualcomm SDA624和MediaTek MT8516等。</p><p>而且，SDK允许设备制造商访问许多原生Android功能。这与谷歌的WiFi和Chromecast产品非常不同，后者没有大量的公共API。结果，谷歌实际上控制着这些设备。</p><p>但好消息是，谷歌正在推出一个统一的物联网平台，保证对Android Things进行更新。当我和Stacey交谈时，她有点失望地听到这些更新仅为期三年。然而，设备制造商将有扩展支持的选项。是的，这比微软Azure Sphere所吹嘘的10年要少，但我认为Android Things更适合消费者设备，技术周期相对较快。希望更新可以通过空中推送，最好是自动进行，这样消费者就不必在应用中点击按钮了。</p><p>当然，要想让Android Things被认为是成功的，谷歌需要公司真正使用它。不出所料，LG和iHome都推出了智能音箱，而我们在CES上看到的智能显示屏也在使用Android Things。</p><p>最初将推出来自联想、LG和JBL的三款智能显示屏，预计将于7月上市。我对在这些设备上看到Android Things感到惊讶，因为从我们在一月份看到的演示来看，它们看起来像是运行传统Android的设备。但这可能有助于解释为什么我们还没有看到它们上市销售：谷歌需要正式将Android Things从开发者预览模式中推出，然后才能推出这些产品。</p><p>请注意，您不会在通常运行Android的设备上看到Android Things。谷歌的Android Things程序政策明确指出了这一点：“您不得将形态因素定义为 Android CDD 设备类型的设备类型（如手持设备、手表、电视、汽车和将来定义的任何其他设备类别）的 Android Things 上构建的产品进行商业化。”</p><p>暂时的其他设备类型都适用于Android Things，因此您可能会看到电视、双向视频门铃、路由器以及其他一些运行谷歌物联网平台的设备。我们将等待硬件制造商创造或原型制作的设备。在这方面，谷歌做得非常容易：您可以在Android Things控制台中管理高达100个非商业用途的设备。之后，需要签订分发协议。</p><p>从平台的角度来看，我认为谷歌在Android Things中拥有满足硬件制造商需求的一切。而且，它还具有广泛的品牌影响力以及实用的云服务，可以提供或处理智能家居中的数据。然而，谷歌稍微有点晚入局。我们可能需要给它6到12个月的时间来看看Android Things是否真的获得了势头。</p><p><a href="https://staceyoniot.com/will-android-things-be-a-big-player-in-iot-ask-me-in-2019/">原文地址</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;随着越来越多的企业看到Android的增长潜力，他们也在越来越多地进行Android移动应用程序开发。Android希望通过发布新功能和更新，如Android Things，继续利用这种增长势头。曾经的Brillo现在成为了Android Things，而Android T</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="IOT" scheme="https://adewu.github.io/tags/IOT/"/>
    
  </entry>
  
  <entry>
    <title>初识React Native</title>
    <link href="https://adewu.github.io/2018/05/02/react_native/"/>
    <id>https://adewu.github.io/2018/05/02/react_native/</id>
    <published>2018-05-01T23:21:00.000Z</published>
    <updated>2024-02-11T12:48:00.817Z</updated>
    
    <content type="html"><![CDATA[<h3 id="什么是React-Native"><a href="#什么是React-Native" class="headerlink" title="什么是React Native"></a>什么是React Native</h3><p>React Native 是一个用于编写真实、原生渲染的 iOS 和 Android 移动应用的 JavaScript 框架。它基于 React，Facebook 的 JavaScript 库用于构建用户界面，但与其针对浏览器不同，它针对移动平台。换句话说：Web 开发人员现在可以使用我们已经了解和喜爱的 JavaScript 库编写真正“原生”的移动应用，而不必离开 JavaScript 的舒适区域。而且，因为大多数代码可以在不同平台之间共享，React Native 让同时为 Android 和 iOS 进行开发变得非常容易。</p><p>与 React for the Web 类似，React Native 应用程序使用 JavaScript 和类似 XML 的标记（称为 JSX）编写。然后，在幕后，React Native “桥接器”调用 Objective-C（用于 iOS）或 Java（用于 Android）中的原生渲染 API。因此，你的应用程序将使用真正的移动 UI 组件进行渲染，而不是 Web 视图，并且看起来和感觉像任何其他移动应用程序一样。React Native 还为平台 API 暴露了 JavaScript 接口，因此你的 React Native 应用程序可以访问平台功能，例如手机摄像头或用户位置。</p><p>React Native 目前支持 iOS 和 Android，还有潜力扩展到未来的平台。在本文中，我们将同时涵盖 iOS 和 Android。我们编写的绝大部分代码将是跨平台的。</p><h3 id="React-Native的优势"><a href="#React-Native的优势" class="headerlink" title="React Native的优势"></a>React Native的优势</h3><p>React Native 实际上使用其宿主平台的标准渲染 API 进行渲染，这使其在跨平台应用程序开发的现有方法中脱颖而出，例如 Cordova 或 Ionic。使用 JavaScript、HTML 和 CSS 的组合编写移动应用程序的现有方法通常使用 webview 进行渲染。虽然这种方法可以工作，但也存在缺点，特别是性能方面。此外，它们通常无法访问宿主平台的一套本地 UI 元素。当这些框架尝试模仿原生 UI 元素时，结果通常感觉“有点差强人意”；逆向工程所有细节，如动画，需要大量的工作，并且它们很快就会过时。</p><p>相比之下，React Native 实际上将你的标记转换为真实的本地 UI 元素，利用了在你正在使用的任何平台上渲染视图的现有方法。此外，React 与主 UI 线程分开工作，因此你的应用程序可以保持高性能而不会牺牲功能。React Native 中的更新周期与 React 中的相同：当 props 或 state 发生变化时，React Native 会重新渲染视图。React Native 与浏览器中的 React 的主要区别在于，React Native 是通过利用其宿主平台的 UI 库来实现这一点，而不是使用 HTML 和 CSS 标记。</p><p>对于习惯于使用 React 在 Web 上工作的开发人员来说，这意味着你可以使用熟悉的工具编写具有原生应用程序性能和外观的移动应用程序。React Native 还在开发体验和跨平台开发潜力方面代表着对正常移动开发的改进。</p><h3 id="开发体验"><a href="#开发体验" class="headerlink" title="开发体验"></a>开发体验</h3><p>如果你以前曾经开发过移动应用程序，你可能会对使用 React Native 的简易性感到惊讶。React Native 团队已经将强大的开发工具和有意义的错误消息集成到框架中，因此使用强大的工具是你开发体验的自然组成部分。</p><p>例如，由于 React Native 只是 JavaScript，你无需重新构建应用程序即可看到你所做的更改反映出来；相反，你可以按下 Command+R 键来刷新你的应用程序，就像你刷新任何其他网页一样。等待应用程序构建的时间可能会积少成多，与之相比，React Native 的快速迭代周期感觉就像是一种天赐良机。</p><p>此外，React Native 还让你可以利用智能调试工具和错误报告功能。如果你熟悉 Chrome 或 Safari 的开发者工具（见图 1-1），你会高兴地知道你也可以将它们用于移动开发。同样，你可以使用你喜欢的任何文本编辑器进行 JavaScript 编辑：React Native 不会强迫你在 Xcode 中开发 iOS，也不会在 Android Studio 中开发 Android。</p><p>除了对你的开发经验进行日常改进外，React Native 还有可能对你的产品发布周期产生积极影响。例如，苹果允许基于 JavaScript 的应用行为变更通过热更新来加载，无需进行额外的审查周期。</p><p>所有这些小的好处加起来可以节省你和其他开发人员的时间和精力，让你能够专注于工作中更有趣的部分，并且整体更加高效。</p><h3 id="代码重用和知识共享"><a href="#代码重用和知识共享" class="headerlink" title="代码重用和知识共享"></a>代码重用和知识共享</h3><p>使用 React Native 可以极大地减少构建移动应用程序所需的资源。任何了解如何编写 React 代码的开发人员现在都可以使用相同的技能集针对 Web、iOS 和 Android 进行开发。通过消除根据目标平台“隔离”开发人员的需要，React Native 让你的团队能够更快地迭代，并更有效地共享知识和资源。</p><p>除了共享知识外，你的大部分代码也可以共享。你编写的并非所有代码都将是跨平台的，根据你在特定平台上需要的功能，你偶尔可能需要涉足 Objective-C 或 Java。但是，使用 React Native 跨平台重新使用代码非常容易。例如，Facebook Ads Manager 应用程序的 Android 版本与 iOS 版本共享了其代码库的 87%，如 React Europe 2015 主题演讲所述。</p><h3 id="风险和缺点"><a href="#风险和缺点" class="headerlink" title="风险和缺点"></a>风险和缺点</h3><p>与任何事物一样，使用 React Native 也并非没有缺点，React Native 是否适合你的团队实际上取决于你的个人情况。</p><p>最大的风险可能是 React Native 的成熟度，因为该项目仍然相对年轻。iOS 支持于 2015 年 3 月发布，Android 支持于 2015 年 9 月发布。文档肯定还有改进的空间，并且仍在不断发展。某些 iOS 和 Android 上的功能仍然不受支持，社区仍在发现最佳实践。</p><p>因为 React Native 在你的项目中引入了另一层，所以它也可能使调试变得更加棘手，特别是在 React 和宿主平台的交集处。</p><p>React Native 仍然年轻，适用于使用新技术的一般注意事项也适用于这里。尽管如此，总的来说，我认为收益大于风险。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>React Native 是一个令人兴奋的框架，它使 Web 开发人员可以利用其现有的 JavaScript 知识创建强大的移动应用程序。它提供了更快的移动开发速度，以及更高效的代码共享，涵盖 iOS、Android 和 Web，而不会牺牲最终用户的体验或应用程序质量。不过，它是新的，仍在不断改进中。如果你的团队能够处理与使用新技术相关的不确定性，并且希望为不止一个平台开发移动应用程序，那么你应该考虑使用 React Native。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;什么是React-Native&quot;&gt;&lt;a href=&quot;#什么是React-Native&quot; class=&quot;headerlink&quot; title=&quot;什么是React Native&quot;&gt;&lt;/a&gt;什么是React Native&lt;/h3&gt;&lt;p&gt;React Native 是一个用于</summary>
      
    
    
    
    <category term="ReactNative" scheme="https://adewu.github.io/categories/ReactNative/"/>
    
    
    <category term="RN" scheme="https://adewu.github.io/tags/RN/"/>
    
  </entry>
  
  <entry>
    <title>Android AOP方案对比</title>
    <link href="https://adewu.github.io/2018/01/12/android_aop/"/>
    <id>https://adewu.github.io/2018/01/12/android_aop/</id>
    <published>2018-01-12T09:21:00.000Z</published>
    <updated>2024-02-11T12:49:41.910Z</updated>
    
    <content type="html"><![CDATA[<h3 id="什么是面向切面编程（Aop）"><a href="#什么是面向切面编程（Aop）" class="headerlink" title="什么是面向切面编程（Aop）"></a>什么是面向切面编程（Aop）</h3><p>面向切面编程（AOP，Aspect-Oriented Programming）是一种编程范式，它允许开发者通过切面（Aspect）来模块化横切关注点，并将它们封装成可重用的组件。横切关注点是指那些存在于应用程序中多个模块中的功能，它们通常与应用程序的核心业务逻辑无关，例如日志记录、事务管理、安全性等。</p><p>在面向切面编程中，切面是一个模块化的单元，它横跨一个或多个对象，提供了在应用程序中的多个位置进行代码重用的机制。切面可以定义一组横切关注点，并将它们与应用程序的核心业务逻辑分离开来。这样，当需要修改或者添加新的横切关注点时，只需要修改或者添加对应的切面，而不需要修改核心业务逻辑代码。</p><p>面向切面编程的核心概念是切面、连接点、通知、切点和增强。其中，切面是关注点的模块化单元，连接点是在应用程序中可以插入切面的点，通知是切面在连接点处执行的代码，切点是一组连接点的集合，而增强是在切点处执行的通知的行为。</p><p>总的来说，面向切面编程通过将横切关注点与核心业务逻辑进行解耦，提高了代码的模块化性、可维护性和可重用性，使得应用程序更易于理解、扩展和维护。</p><h3 id="主流AOP技术分类"><a href="#主流AOP技术分类" class="headerlink" title="主流AOP技术分类"></a>主流AOP技术分类</h3><p><strong>首先</strong>，从织入的时机的角度看，可以分为源码阶段、class阶段、dex阶段、运行时织入。</p><p>对于前三项<strong>源码阶段、class阶段、dex织入</strong>，由于他们都发生在class加载到虚拟机前，我们统称为静态织入， 而在运行阶段发生的改动，我们统称为动态织入。</p><p>静态织入：</p><ul><li>APT</li><li>AspectJ</li><li>ASM</li><li>Javassit</li></ul><p>动态织入：</p><ul><li>java动态代理</li><li>cglib</li><li>Javassit</li></ul><p>静态织入发生在编译器，因此几乎不会对运行时的效率产生影响；动态织入发生在运行期，可直接将字节码写入内存，并通过反射完成类的加载，所以效率相对较低，但更灵活。</p><p><strong>其次</strong>，我们需要关注这些框架具备哪切面编程的能力，这有助于帮助我做技术选型，由于AspectJ、ASM 、Javassit是相对比较完善的AOP框架，因此只对三者进行比较。</p><table><thead><tr><th><strong>能力</strong></th><th><strong>AspectJ</strong></th><th><strong>ASM</strong></th><th><strong>Javassit</strong></th></tr></thead><tbody><tr><td>切面抽象</td><td>✅</td><td></td><td></td></tr><tr><td>切点抽象</td><td></td><td>✅</td><td></td></tr><tr><td>通知类型抽象</td><td>✅</td><td>✅</td><td>✅</td></tr></tbody></table><p>其中：</p><ul><li>切面抽象：具备筛选过滤class的能力，比如我们想为Activity的所有生命周期织入代码，那你是不是首先需要具备过滤Activity及其子类的能力。</li><li>切点抽象：具体到某个class，是否具备方法、字段、注解访问的能力。</li><li>通知类型抽象：是否直接支持在方法前、后、中直接织入代码。</li></ul><p>当然不具备能力不代表不能做AOP编程，可以通过其他方法解决，只是易用性的问题。</p><h3 id="APT"><a href="#APT" class="headerlink" title="APT"></a>APT</h3><p>APT 是 Annotation Processing Tool 的缩写，即注解处理工具。APT 是一种工具，用于在编译时处理 Java 源代码中的注解，生成额外的 Java 代码或者其他文件，以帮助开发者完成一些重复性的工作或者自动生成一些代码。</p><p>通过 APT，开发者可以定义自己的注解，并编写相应的处理器来处理这些注解。在编译时，APT 会扫描源代码中的注解，并调用相应的处理器来生成额外的代码或者其他文件。这些生成的代码可以用于实现一些功能，如自动实现 Parcelable 接口、生成单例模式的代码、自动生成代理类等。</p><p>APT 在 Android 开发中有着广泛的应用，例如 ButterKnife 库就是利用 APT 实现的，它通过在 View 属性上添加注解，然后在编译时生成相应的 View 绑定代码，从而简化了代码编写。另外，ButterKnife、Dagger等一些常用的 Android 库也使用了 APT 来生成代码。</p><p>总的来说，APT 是一种强大的工具，可以在编译时自动化处理一些重复性的工作，提高了开发效率，减少了开发者的工作量。</p><h3 id="AspectJ"><a href="#AspectJ" class="headerlink" title="AspectJ"></a>AspectJ</h3><p>AspectJ 是一个基于 Java 语言的面向切面编程（AOP）的扩展框架，它提供了一种简洁而强大的方式来实现横切关注点的模块化和重用。</p><p>AspectJ 扩展了 Java 语言，引入了一组新的关键字和语法规则，使得开发者可以更方便地定义切面和通知，并将它们应用到代码中的特定连接点上。AspectJ 通过编译时的静态代码织入技术，将切面的行为嵌入到应用程序的字节码中，从而实现了对横切关注点的有效管理和控制。</p><p>使用 AspectJ，开发者可以通过注解或者 XML 配置文件来定义切面，并使用 Pointcut 表达式来指定切入点（即横切关注点所在的位置）。然后，开发者可以编写 Advice（通知）来定义切面在连接点处执行的代码逻辑，例如在方法调用之前或之后执行的操作。</p><p>举个简单的例子，假设我们想统计所有view的点击事件，使用AspectJ只需要写一个类即可。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MethodAspect</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">TAG</span> <span class="operator">=</span> <span class="string">&quot;MethodAspect5&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//切面表达式，声明需要过滤的类和方法 </span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* android.view.View.OnClickListener+.onClick(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">callMethod</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//before表示在方法调用前织入</span></span><br><span class="line">    <span class="meta">@before(&quot;callMethod()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">beforeMethodCall</span><span class="params">(ProceedingJoinPoint joinPoint)</span> &#123;</span><br><span class="line">    <span class="comment">//编写业务代码</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="javassit"><a href="#javassit" class="headerlink" title="javassit"></a>javassit</h3><p>Javassist 是一个 Java 字节码操作库，它提供了一种方便的方式来动态修改类的字节码，生成新的类或者修改已有类的行为。Javassist 的全称是 Java Programming Assistant，它允许开发者在运行时动态地创建、修改和操作 Java 类，而无需直接操作字节码。</p><p>Javassist 提供了一组简洁而强大的 API，使得开发者可以在运行时对类进行各种操作，例如创建新的类、修改已有类的字段、方法或者构造函数、添加新的方法、修改方法的实现、以及执行类的加载和热替换等。通过 Javassist，开发者可以实现诸如 AOP、动态代理、类加载器、字节码增强等各种功能。</p><p>以下是 Javassist 的简单用例，演示了如何使用 Javassist 创建一个新的类，并添加一个新的方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javassist.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JavassistExample</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">// 创建类池</span></span><br><span class="line">        <span class="type">ClassPool</span> <span class="variable">classPool</span> <span class="operator">=</span> ClassPool.getDefault();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建一个新的类</span></span><br><span class="line">        <span class="type">CtClass</span> <span class="variable">newClass</span> <span class="operator">=</span> classPool.makeClass(<span class="string">&quot;ExampleClass&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 添加一个新的方法</span></span><br><span class="line">        <span class="type">CtMethod</span> <span class="variable">newMethod</span> <span class="operator">=</span> CtNewMethod.make(<span class="string">&quot;public void hello() &#123; System.out.println(\&quot;Hello, Javassist!\&quot;); &#125;&quot;</span>, newClass);</span><br><span class="line">        newClass.addMethod(newMethod);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 将新类加载到内存中</span></span><br><span class="line">        Class&lt;?&gt; clazz = newClass.toClass();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 实例化新类并调用方法</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> clazz.newInstance();</span><br><span class="line">        clazz.getMethod(<span class="string">&quot;hello&quot;</span>).invoke(obj);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="ASM"><a href="#ASM" class="headerlink" title="ASM"></a>ASM</h3><p>ASM 是一个 Java 字节码操作和分析框架，它允许开发者在编译时或运行时动态生成或修改 Java 类的字节码。ASM 的全称是 Java Bytecode Engineering Library，它提供了一组简单而强大的 API，使得开发者可以直接操作字节码，从而实现对 Java 类的各种高级操作。</p><p>ASM 具有轻量级、高性能和灵活性的特点，它的设计目标是尽可能地接近 Java 字节码的底层结构，以最小的开销实现对字节码的操作。ASM 提供了一种基于访问者模式的 API，开发者可以通过实现不同的访问者接口来遍历、解析和修改类的字节码，从而实现对类的各种操作，如新增、删除、修改字段、方法、注解等。</p><p>ASM 可以广泛应用于 Java 字节码的动态生成、增强和优化等领域，例如动态代理、AOP（面向切面编程）、代码生成器、类加载器、代码混淆、性能优化等。由于其高性能和灵活性，ASM 被广泛应用于各种 Java 框架和工具中，如 Spring、Hibernate、Mockito 等。</p><p>完整的学习ASM必须了解字节码和JVM相关知识。比如要织入一句简单的日志输出：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Log.d(<span class="string">&quot;tag&quot;</span>, <span class="string">&quot; onCreate&quot;</span>);</span><br></pre></td></tr></table></figure><p>使用ASM编写是下面这个样子：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mv.visitLdcInsn(<span class="string">&quot;tag&quot;</span>);</span><br><span class="line">mv.visitLdcInsn(<span class="string">&quot;onCreate&quot;</span>);</span><br><span class="line">mv.visitMethodInsn(INVOKESTATIC, <span class="string">&quot;android/util/Log&quot;</span>, <span class="string">&quot;d&quot;</span>, <span class="string">&quot;(Ljava/lang/String;Ljava/lang/String;)I&quot;</span>, <span class="literal">false</span>);</span><br><span class="line">mv.visitInsn(POP);</span><br></pre></td></tr></table></figure><p>可以看出ASM与AspectJ有很大的不同，AspectJ织入的代码就是实际编写的代码，但ASM必须使用其提供的API编写指令。一行java代码可能对应多行ASM API代码，因为一行java代码背后可能隐藏这多个JVM指令。</p><h3 id="动态代理"><a href="#动态代理" class="headerlink" title="动态代理"></a>动态代理</h3><p>动态代理是代理模式的一种实现，用于在运行时动态增强原始类的行为，实现方式是运行时直接生成class字节码并将其加载进虚拟机。</p><p>JDK本身就提供一个Proxy类用于实现动态代理。 我们通常使用下面的API创建代理类。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//java.lang.reflect.Proxy</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">newProxyInstance</span><span class="params">(ClassLoader loader,</span></span><br><span class="line"><span class="params">    Class&lt;?&gt;[] interfaces, </span></span><br><span class="line"><span class="params">    InvocationHandler h)</span></span><br></pre></td></tr></table></figure><p>其中在InvocationHandler实现类中定义核心切点代码。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InvocationHandlerImpl</span> <span class="keyword">implements</span> <span class="title class_">InvocationHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 被代理的实例 */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Object</span> <span class="variable">mObj</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">InvocationHandlerImpl</span><span class="params">(Object obj)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.mObj = obj;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        <span class="comment">//前切入点</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> method.invoke(<span class="built_in">this</span>.mObj, args);</span><br><span class="line">        <span class="comment">//后切入点</span></span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>java动态代理最大的问题是只能代理接口，而不能代理普通类或者抽象类，这是因为默认创建的代理类继承Porxy，而java又不支持多继承，这一点极大的限制了动态代理的使用场景，cglib可代理普通类。</p><h3 id="AOP框架的特点及优劣势"><a href="#AOP框架的特点及优劣势" class="headerlink" title="AOP框架的特点及优劣势"></a>AOP框架的特点及优劣势</h3><p><img src="/../imgs/android_aop_compare.png" alt="alt text" title="aop对比"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;什么是面向切面编程（Aop）&quot;&gt;&lt;a href=&quot;#什么是面向切面编程（Aop）&quot; class=&quot;headerlink&quot; title=&quot;什么是面向切面编程（Aop）&quot;&gt;&lt;/a&gt;什么是面向切面编程（Aop）&lt;/h3&gt;&lt;p&gt;面向切面编程（AOP，Aspect-Orie</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="Aop" scheme="https://adewu.github.io/tags/Aop/"/>
    
  </entry>
  
  <entry>
    <title>Android Binder机制解析</title>
    <link href="https://adewu.github.io/2017/09/26/android_binder/"/>
    <id>https://adewu.github.io/2017/09/26/android_binder/</id>
    <published>2017-09-26T03:22:00.000Z</published>
    <updated>2024-02-11T12:49:33.962Z</updated>
    
    <content type="html"><![CDATA[<p>在Android中跨进程通信被频繁使用，比如当用户点击按钮跳转到一个新的Activity页面，这时我们的app进程会和SystemServer进程频繁通信来创建一个新的Activity，这种例子不胜枚举。所以作为一个Android应用的开发者，理应理解其原理。在讨论Binder前，我们先列举下Linux支持的进程通信方式都有哪些。</p><p>Android进程间通信场景</p><p>Linux进程通信方式</p><p>为什么要自己实现ipc</p><p>通过对比，分别介绍优点</p><p>使用？</p><p>上层拓展？</p><h3 id="Linux进程通信方式"><a href="#Linux进程通信方式" class="headerlink" title="Linux进程通信方式"></a>Linux进程通信方式</h3><ul><li>管道( pipe )：管道是一种半双工的通信方式，数据只能单向流动，而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。</li><li>命名管道 (named pipe) ： 命名管道也是半双工的通信方式，但是它允许无亲缘关系进程间的通信。</li><li>信号量( semophore ) ： 信号量是一个计数器，可以用来控制多个进程对共享资源的访问。它常作为一种锁机制，防止某进程正在访问共享资源时，其他进程也访问该资源。因此，主要作为进程间以及同一进程内不同线程之间的同步手段。</li><li>消息队列( message queue ) ： 消息队列是由消息的链表，存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。</li><li>信号 ( signal ) ： 信号是一种比较复杂的通信方式，用于通知接收进程某个事件已经发生。</li><li>共享内存( shared memory)：共享内存就是映射一段能被其他进程所访问的内存，这段共享内存由一个进程创建，但多个进程都可以访问。共享内存是快的IPC方式，它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制，如信号量，配合使用，来实现进程间的同步和通信。</li><li>套接字( socket ) ： 套解口也是一种进程间通信机制，与其他通信机制不同的是，它可用于不同及其间的进程通信。</li><li>文件(File)：文件系统对文件描述符的读&#x2F;写控制，进程间一方对文件写，一方对文件读，达到文件之间的通信，可以是不相关进程间的通信。</li></ul><h3 id="Android为什么要自己实现ipc"><a href="#Android为什么要自己实现ipc" class="headerlink" title="Android为什么要自己实现ipc"></a>Android为什么要自己实现ipc</h3><ul><li>性能：在移动设备上，由于性能受到各方面的限制，例如电量，所以在使用进程间通信时，需要对性能这块有很大的要求。Socket、消息队列这些，在进程间通信时，数据拷贝需要两次，而内存共享只需拷贝无需拷贝，但实现起来比较复杂。Binder在数据上拷贝只需一次，它通过内存映射的机制实现了一次数据拷贝。</li><li>安全：传统的IPC，UID&#x2F;PID只能由用户在数据包填入，这样会导致接收方无法获取到可靠UID&#x2F;PID，而且还会存在被恶意程序利用的可能。Binder机制中可靠的身份标志只能在IPC机制的内核中添加的，它从协议本身就支持对通信双方进行身份校验，大大提高了安全性。</li><li>稳定：Binder基于C&#x2F;S架构，使用者与提供服务者相互独立，整体清晰，从而提高了整体的稳定性。</li></ul><p>下表是Binder和主要Linux进程通信方式的对比：</p><table><thead><tr><th>IPC方式</th><th>效率</th><th>单双向</th><th>模型</th><th>安全性</th></tr></thead><tbody><tr><td>Binder</td><td>高</td><td>双向</td><td>多堆多</td><td>✅</td></tr><tr><td>Pipe</td><td>一般</td><td>单向</td><td>一对一</td><td>✅</td></tr><tr><td>共享内存</td><td>最高</td><td>双向</td><td>多堆多</td><td>❎</td></tr><tr><td>Socket</td><td>很低</td><td>双向</td><td>多堆多</td><td>✅</td></tr><tr><td>File</td><td>低</td><td>双向</td><td>多堆多</td><td>❎</td></tr></tbody></table><h4 id="Binder"><a href="#Binder" class="headerlink" title="Binder"></a>Binder</h4><p>Binder 是基于内存映射 mmap 设计实现的，我们需要先了解一下 mmap 的概念。</p><h5 id="mmap"><a href="#mmap" class="headerlink" title="mmap"></a>mmap</h5><p><code>mmap</code> 是一个 Unix 系统调用，用于将文件或设备映射到进程的地址空间，从而允许进程通过内存访问文件内容或设备数据。<code>mmap</code> 的全称是 “memory map”，它将文件或设备的一部分直接映射到进程的地址空间中，使得对该部分数据的读写操作可以像访问内存一样简单高效。</p><p>通过 <code>mmap</code>，进程可以将文件映射到内存中的一个地址区域，然后直接读写该区域的数据，而不需要通过传统的文件 I&#x2F;O 接口，例如 <code>read</code> 和 <code>write</code>。这样可以提高 I&#x2F;O 操作的性能，特别是对于大文件的处理。此外，<code>mmap</code> 还可以用于创建共享内存区域，使得不同进程之间可以共享数据。</p><p>在使用 <code>mmap</code> 后，可以通过修改内存区域的内容来修改文件内容，这种修改会直接反映到原始文件中。因此，<code>mmap</code> 也可以用于实现文件的原子写入或原子更新操作。</p><p>总之，<code>mmap</code> 提供了一种灵活高效的方式来访问文件内容或设备数据，并且可以简化对文件和内存的管理和操作。</p><h5 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h5><p>一次 Binder IPC 通信的过程分为以下几个步骤：</p><ol><li><p>首先，Binder 驱动在内核空间中开辟出一个数据接收缓冲区</p></li><li><p>接着，在内核空间中开辟出一个内核缓冲区</p></li><li><p>将内核缓冲区与数据接收缓冲区建立映射关系</p></li><li><p>将数据接收缓冲区与接收进程的用户空间地址建立映射关系</p></li><li><p>发送方进程通过 copy_from_user 将数据从用户空间复制到内核缓冲区</p></li><li><p>由于内核缓冲区与数据接收缓冲区有映射关系，同时数据接收缓冲区与接收进程的用户空间地址有映射关系，所以在接收进程中可以直接获取到这段数据</p></li></ol><p>这样便完成了一次 Binder IPC 通信，它的原理如下图所示：</p><p><img src="/../imgs/android_binder_process.png" alt="alt text" title="通信流程"></p><p>通过 mmap，Binder 通信时，只需要经历一次数据复制，性能要优于管道、消息队列、socket等方式，在安全性，易用性方面又优于共享内存。鉴于上述原因，Android 选择了这种折中的 IPC 方式，来满足系统对稳定性、传输性能和安全性方面的要求。</p><h3 id="Binder架构"><a href="#Binder架构" class="headerlink" title="Binder架构"></a>Binder架构</h3><p>Binder 是一种 C&#x2F;S 架构，分为 BpBinder（客户端）和 BBinder（服务端），他们都派生自 IBinder。其中 BpBinder 中的p表示 proxy，即代理。BpBinder 通过 transact 来发送事务请求，BBinder 通过 onTransact 来接收相应的事务。</p><p><img src="/../imgs/android_ibinder.png" alt="alt text" title="通信流程"></p><p>Binder 采用分层架构设计：</p><p><img src="/../imgs/android_binder_arch.png" alt="alt text" title="通信流程"></p><p>Binder 一次通信的时序图如下：</p><p><img src="/../imgs/android_binder_timeline.png" alt="alt text" title="通信流程"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在Android中跨进程通信被频繁使用，比如当用户点击按钮跳转到一个新的Activity页面，这时我们的app进程会和SystemServer进程频繁通信来创建一个新的Activity，这种例子不胜枚举。所以作为一个Android应用的开发者，理应理解其原理。在讨论Bind</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="Binder" scheme="https://adewu.github.io/tags/Binder/"/>
    
  </entry>
  
  <entry>
    <title>自定义View流程</title>
    <link href="https://adewu.github.io/2017/09/20/android_custom_view/"/>
    <id>https://adewu.github.io/2017/09/20/android_custom_view/</id>
    <published>2017-09-20T11:52:00.000Z</published>
    <updated>2024-02-11T12:49:16.127Z</updated>
    
    <content type="html"><![CDATA[<p>Android系统提供了丰富的视图组件，如TextView、ImageView、Button等，以及一些容器组件，如ConstraintLayout、LinearLayout、FrameLayout等，使用这些组件可以实现各种常见的视图效果。然而，在某些情况下，我们可能需要实现更加个性化和独特的展示效果，而系统提供的组件很难满足这种需求。这时候，就需要使用自定义View来实现了。</p><p>自定义View的实现流程大致分为以下几方面：</p><ol><li>创建自定义View类继承自View&#x2F;现有控件类</li><li>View的测量过程onMeasure</li><li>View的绘制过程onDraw</li></ol><p>下面将依序实现各流程</p><h3 id="创建自定义View类"><a href="#创建自定义View类" class="headerlink" title="创建自定义View类"></a>创建自定义View类</h3><h4 id="继承View创建一个自定义View类"><a href="#继承View创建一个自定义View类" class="headerlink" title="继承View创建一个自定义View类"></a>继承View创建一个自定义View类</h4><p>自定义View类可以直接继承自View实现，也可以根据业务场景直接继承自特定的控件。</p><h4 id="重写构造方法"><a href="#重写构造方法" class="headerlink" title="重写构造方法"></a>重写构造方法</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyView</span> <span class="keyword">extends</span> <span class="title class_">View</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyView</span><span class="params">(Context context)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(context, <span class="literal">null</span>);</span><br><span class="line">        init(context);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyView</span><span class="params">(Context context, <span class="meta">@Nullable</span> AttributeSet attrs)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(context, attrs);</span><br><span class="line">        init(context);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="定义View的自定义属性"><a href="#定义View的自定义属性" class="headerlink" title="定义View的自定义属性"></a>定义View的自定义属性</h4><p>自定义属性属于styleable，这样我们就可以在xml布局文件中直接声明自定义View的属性来增加View的灵活性</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">resources</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">declare-styleable</span> <span class="attr">name</span>=<span class="string">&quot;MyView&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">attr</span> <span class="attr">name</span>=<span class="string">&quot;MyText&quot;</span> <span class="attr">format</span>=<span class="string">&quot;string|reference&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">attr</span> <span class="attr">name</span>=<span class="string">&quot;MySize&quot;</span> <span class="attr">format</span>=<span class="string">&quot;dimension|reference&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">declare-styleable</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resources</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="使用自定义属性"><a href="#使用自定义属性" class="headerlink" title="使用自定义属性"></a>使用自定义属性</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">(Context context, AttributeSet attrs, <span class="type">int</span> defStyleAttr)</span> &#123;</span><br><span class="line">    <span class="type">TypedArray</span> <span class="variable">typedArray</span> <span class="operator">=</span> context.obtainStyledAttributes(attrs,</span><br><span class="line">            R.styleable.MyView, defStyleAttr, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    text = typedArray.getString(R.styleable.MyView_customText);</span><br><span class="line">    dimension = typedArray.getDimension(R.styleable.MyView_customSize, <span class="number">30</span>);</span><br><span class="line"></span><br><span class="line">    typedArray.recycle();</span><br><span class="line">  </span><br><span class="line"> <span class="comment">//执行初始化逻辑</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="自定义View的测量：onMeasure"><a href="#自定义View的测量：onMeasure" class="headerlink" title="自定义View的测量：onMeasure"></a>自定义View的测量：onMeasure</h3><h4 id="MeasureSpec"><a href="#MeasureSpec" class="headerlink" title="MeasureSpec"></a>MeasureSpec</h4><p>MeasureSpec是View的内部类，它封装了一个View的尺寸，在<code>onMeasure()</code>当中会根据这个<code>MeasureSpec</code>的值来确定View的宽高。</p><p>MeasureSpec的值保存在一个int值当中。一个int值有32位，前两位表示模式<code>mode</code>后30位表示<code>大小size</code>。即<code>MeasureSpec</code> &#x3D;<code>mode</code> + <code>size</code>。</p><p>在<code>MeasureSpec</code>当中一共存在三种mode：<code>UNSPECIFIED</code>、<code>EXACTLY</code> 和 <code>AT_MOST</code>。</p><p><strong>MeasureSpec.EXACTLY</strong>  </p><p>在布局中指定了宽高 是一个确切的值（比如100dp 可以直接使用）对应match_parent。</p><p><strong>MeasureSpec.ATMOST</strong></p><p>AT_MOST是最大的意思，这时需要测量自己控件需要的的宽高，如果大于父控件的，那么就用父控件的大小，如果小于父控件，就使用父控件大小  总结下来就是尽可能地给最大 但是不能超过父控件。对应wrap_content。</p><p><strong>MeasureSpec.UNSPECIFIED</strong></p><p>表示尽可能大的显示在listview和scrollview中就是这样测量宽高的，因为是滚动控件，一般自己定义时很少用到 看scrollview的源码会看到它的模式是获取最大的显示方式。</p><p>通常，可以使用<code>MeasureSpec</code>类的<code>getSize()</code>和<code>getMode()</code>方法来获取宽度和高度的建议值以及测量模式。调用setMeasuredDimension()方法将计算出的宽高传入。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取测量模式（Mode）</span></span><br><span class="line"><span class="type">int</span> <span class="variable">specMode</span> <span class="operator">=</span> MeasureSpec.getMode(measureSpec)</span><br><span class="line"><span class="comment">// 获取测量大小（Size）</span></span><br><span class="line"><span class="type">int</span> <span class="variable">specSize</span> <span class="operator">=</span> MeasureSpec.getSize(measureSpec)</span><br><span class="line"><span class="comment">// 通过Mode 和 Size 生成新的SpecMode</span></span><br><span class="line"><span class="type">int</span> measureSpec=MeasureSpec.makeMeasureSpec(size, mode);</span><br></pre></td></tr></table></figure><p>在View当中，<code>MeasureSpace</code>的测量代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">getChildMeasureSpec</span><span class="params">(<span class="type">int</span> spec, <span class="type">int</span> padding, <span class="type">int</span> childDimension)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">specMode</span> <span class="operator">=</span> MeasureSpec.getMode(spec);</span><br><span class="line">        <span class="type">int</span> <span class="variable">specSize</span> <span class="operator">=</span> MeasureSpec.getSize(spec);</span><br><span class="line">        <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> Math.max(<span class="number">0</span>, specSize - padding);</span><br><span class="line">        <span class="type">int</span> <span class="variable">resultSize</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">resultMode</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">switch</span> (specMode) &#123;</span><br><span class="line">        <span class="comment">//当父View要求一个精确值时，为子View赋值</span></span><br><span class="line">        <span class="keyword">case</span> MeasureSpec.EXACTLY:</span><br><span class="line">            <span class="comment">//如果子view有自己的尺寸，则使用自己的尺寸</span></span><br><span class="line">            <span class="keyword">if</span> (childDimension &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                resultSize = childDimension;</span><br><span class="line">                resultMode = MeasureSpec.EXACTLY;</span><br><span class="line">                <span class="comment">//当子View是match_parent,将父View的大小赋值给子View</span></span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (childDimension == LayoutParams.MATCH_PARENT) &#123;</span><br><span class="line">                resultSize = size;</span><br><span class="line">                resultMode = MeasureSpec.EXACTLY;</span><br><span class="line">                <span class="comment">//如果子View是wrap_content，设置子View的最大尺寸为父View</span></span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (childDimension == LayoutParams.WRAP_CONTENT) &#123;</span><br><span class="line">                resultSize = size;</span><br><span class="line">                resultMode = MeasureSpec.AT_MOST;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="comment">// 父布局给子View了一个最大界限</span></span><br><span class="line">        <span class="keyword">case</span> MeasureSpec.AT_MOST:</span><br><span class="line">            <span class="keyword">if</span> (childDimension &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">//如果子view有自己的尺寸，则使用自己的尺寸</span></span><br><span class="line">                resultSize = childDimension;</span><br><span class="line">                resultMode = MeasureSpec.EXACTLY;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (childDimension == LayoutParams.MATCH_PARENT) &#123;</span><br><span class="line">                <span class="comment">// 父View的尺寸为子View的最大尺寸</span></span><br><span class="line">                resultSize = size;</span><br><span class="line">                resultMode = MeasureSpec.AT_MOST;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (childDimension == LayoutParams.WRAP_CONTENT) &#123;</span><br><span class="line">                <span class="comment">//父View的尺寸为子View的最大尺寸</span></span><br><span class="line">                resultSize = size;</span><br><span class="line">                resultMode = MeasureSpec.AT_MOST;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="comment">// 父布局对子View没有做任何限制</span></span><br><span class="line">        <span class="keyword">case</span> MeasureSpec.UNSPECIFIED:</span><br><span class="line">            <span class="keyword">if</span> (childDimension &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">//如果子view有自己的尺寸，则使用自己的尺寸</span></span><br><span class="line">                resultSize = childDimension;</span><br><span class="line">                resultMode = MeasureSpec.EXACTLY;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (childDimension == LayoutParams.MATCH_PARENT) &#123;</span><br><span class="line">                <span class="comment">//因父布局没有对子View做出限制，当子View为MATCH_PARENT时则大小为0</span></span><br><span class="line">                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? <span class="number">0</span> : size;</span><br><span class="line">                resultMode = MeasureSpec.UNSPECIFIED;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (childDimension == LayoutParams.WRAP_CONTENT) &#123;</span><br><span class="line">                <span class="comment">//因父布局没有对子View做出限制，当子View为WRAP_CONTENT时则大小为0</span></span><br><span class="line">                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? <span class="number">0</span> : size;</span><br><span class="line">                resultMode = MeasureSpec.UNSPECIFIED;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> MeasureSpec.makeMeasureSpec(resultSize, resultMode);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="自定义View的绘制：onDraw"><a href="#自定义View的绘制：onDraw" class="headerlink" title="自定义View的绘制：onDraw"></a>自定义View的绘制：onDraw</h3><p>draw流程也就是把View绘制到屏幕上的过程，整个流程的入口在<code>View</code>的<code>draw()</code>方法之中，而源码注释也写的很明白，整个过程可以分为6个步骤。</p><ol><li>如果需要，绘制背景。</li><li>有过有必要，保存当前canvas。</li><li>绘制View的内容。</li><li>绘制子View。</li><li>如果有必要，绘制边缘、阴影等效果。</li><li>绘制装饰，如滚动条等等。</li></ol><p>自定义控件被创建并且测量代码写好后，接下来就调用onDraw()来绘制View。以下是一个绘制圆形的示例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">onDraw</span><span class="params">(Canvas canvas)</span> &#123;</span><br><span class="line">      <span class="comment">//调用父类的onDraw函数，因为View这个类实现了一些基本的绘制功能，比如绘制背景颜色和背景图片</span></span><br><span class="line">      <span class="built_in">super</span>.onDraw(canvas);</span><br><span class="line">      <span class="comment">//半径</span></span><br><span class="line">      <span class="type">int</span> <span class="variable">r</span> <span class="operator">=</span> getMeasuredWidth()/<span class="number">2</span>;</span><br><span class="line">      <span class="type">int</span> <span class="variable">centerX</span> <span class="operator">=</span> getLeft() + r;</span><br><span class="line">      <span class="type">int</span> <span class="variable">centerY</span> <span class="operator">=</span> getTop() + r;</span><br><span class="line">      canvas.drawCircle(centerX,centerY,r,mPaint);</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Android系统提供了丰富的视图组件，如TextView、ImageView、Button等，以及一些容器组件，如ConstraintLayout、LinearLayout、FrameLayout等，使用这些组件可以实现各种常见的视图效果。然而，在某些情况下，我们可能需要</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="自定义View" scheme="https://adewu.github.io/tags/%E8%87%AA%E5%AE%9A%E4%B9%89View/"/>
    
  </entry>
  
  <entry>
    <title>WebView加载提速方案</title>
    <link href="https://adewu.github.io/2017/07/12/android_webview/"/>
    <id>https://adewu.github.io/2017/07/12/android_webview/</id>
    <published>2017-07-12T01:52:00.000Z</published>
    <updated>2024-02-11T12:48:55.999Z</updated>
    
    <content type="html"><![CDATA[<p>以公司的新闻资讯类产品为例，总结一下加载提速方案。<br>首先对于新闻资讯页面来说，不同的页面之间有很多重复的资源，这些资源可以直接保存在本地复用：将css、js和图片文件放到Assets中，应用可以拦截这部分的请求转而加载这些文件。<br>其次，WebView的新建与初始化也是比较耗时的，因此可以使用WebView缓存池进行复用。<br>再次是预加载，在浏览信息流时，详情页url的主要html内容已经被下载到了内存中，可以直接通过<code>WebView.loadDataWithBaseUrl()</code>加载。接下来介绍这3种方案的具体实现。</p><h3 id="资源缓存"><a href="#资源缓存" class="headerlink" title="资源缓存"></a>资源缓存</h3><p>WebView 请求任何资源时都会回调shouldInterceptRequest()方法，此时可以将 WebView 需要请求的资源替换为本地资源以提升加载速度。<br> 本地资源可以保存在Assets或文件系统中，如果保存在Assets中，文件的安全性会得到保证，没有出错或者被修改的风险，但是无法实时更新，只能依赖客户端发版更新文件；如果保存在文件系统中，可以做到实时更新，但是下载文件时可能出错，使用时需要对文件进行校验。</p><p>这里以保存在Assets中为例，当WebView回调shouldInterceptRequest()时，如果发现当前的文件可以使用Assets中的缓存，即可将其包装成WebResourceResponse，如下所示。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">mWebView.setWebViewClient(<span class="keyword">new</span> <span class="title class_">WebViewClient</span>() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> WebResourceResponse <span class="title function_">shouldInterceptRequest</span><span class="params">(WebView view, WebResourceRequest request)</span> &#123;</span><br><span class="line">        <span class="comment">// 根据url得到文件名</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">fileName</span> <span class="operator">=</span> getFileNameByUrl(request.getUrl().toString());</span><br><span class="line">        <span class="keyword">if</span> (!fileInCache(fileName)) &#123;</span><br><span class="line">            <span class="comment">// 如果当前文件不在缓存列表中, 不使用缓存</span></span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">super</span>.shouldInterceptRequest(view, request);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 当前文件可以使用缓存, 根据后缀判断 mimetype</span></span><br><span class="line">            <span class="type">InputStream</span> <span class="variable">inputStream</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="type">String</span> <span class="variable">mimeType</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">if</span> (fileName.endsWith(<span class="string">&quot;css&quot;</span>)) &#123;</span><br><span class="line">                mimeType = <span class="string">&quot;text/css&quot;</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (fileName.endsWith(<span class="string">&quot;js&quot;</span>)) &#123;</span><br><span class="line">                mimeType = <span class="string">&quot;text/javascript&quot;</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (fileName.endsWith(<span class="string">&quot;png&quot;</span>)) &#123;</span><br><span class="line">                mimeType = <span class="string">&quot;image/png&quot;</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (mimeType == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                inputStream = App.getContext().getAssets().open(fileName);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                Log.e(LOG_TAG, <span class="string">&quot;read file IOException: &quot;</span> + e.getMessage());</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (inputStream != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="type">WebResourceResponse</span> <span class="variable">response</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WebResourceResponse</span>(</span><br><span class="line">                            mimeType, <span class="string">&quot;utf-8&quot;</span>, inputStream);</span><br><span class="line">                <span class="comment">// 解决css、js的跨域问题</span></span><br><span class="line">                Map&lt;String, String&gt; headers = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">                headers.put(<span class="string">&quot;Access-Control-Allow-Origin&quot;</span>, <span class="string">&quot;*&quot;</span>);</span><br><span class="line">                headers.put(<span class="string">&quot;Access-Control-Allow-Methods&quot;</span>, <span class="string">&quot;POST, GET, OPTIONS, DELETE, PUT&quot;</span>);</span><br><span class="line">                headers.put(<span class="string">&quot;Access-Control-Max-Age&quot;</span>, <span class="string">&quot;3600&quot;</span>);</span><br><span class="line">                headers.put(<span class="string">&quot;Access-Control-Allow-Headers&quot;</span>, <span class="string">&quot;Content-Type,Access-Token,Authorization&quot;</span>);</span><br><span class="line">                response.setResponseHeaders(headers);</span><br><span class="line">                <span class="keyword">return</span> response;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="WebView缓存池"><a href="#WebView缓存池" class="headerlink" title="WebView缓存池"></a>WebView缓存池</h3><p>平时使用WebView的时候我们都是动态新建并添加到ViewGroup中，新建时就传入了Context，那如果使用缓存池，Context该怎么传呢？</p><p>一种方案是直接用ApplicationContext新建WebView；另一种方案是使用MutableContextWrapper，如果在某个Activity中被使用了就改为该Activity的Context，回收时改为ApplicationContext。一个简易版的WebView缓存池实现如下。</p><p>注意在APP启动时应该就要调用WebViewPool的初始化方法新建一个WebView，不然启动第一个包含WebView的页面时耗时也会很久。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebViewPool</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;DetailWebView&gt; mIdleWebViewList;</span><br><span class="line">    <span class="keyword">private</span> List&lt;DetailWebView&gt; mUsingWebViewList;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Holder</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">WebViewPool</span> <span class="variable">sInstance</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WebViewPool</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> WebViewPool <span class="title function_">getInstance</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Holder.sInstance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">WebViewPool</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 在 APP 启动时调用, 直接新建一个备用的WebView</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span> &#123;</span><br><span class="line">        mIdleWebViewList = <span class="keyword">new</span> <span class="title class_">CopyOnWriteArrayList</span>&lt;&gt;();</span><br><span class="line">        mUsingWebViewList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="type">MutableContextWrapper</span> <span class="variable">contextWrapper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MutableContextWrapper</span>(App.getContext());</span><br><span class="line">        <span class="type">DetailWebView</span> <span class="variable">detailWebView</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DetailWebView</span>(contextWrapper);</span><br><span class="line">        mIdleWebViewList.add(detailWebView);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> DetailWebView <span class="title function_">acquireWebView</span><span class="params">(Context context)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (mIdleWebViewList != <span class="literal">null</span> &amp;&amp; mIdleWebViewList.size() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="type">DetailWebView</span> <span class="variable">webView</span> <span class="operator">=</span> mIdleWebViewList.remove(<span class="number">0</span>);</span><br><span class="line">            <span class="type">MutableContextWrapper</span> <span class="variable">contextWrapper</span> <span class="operator">=</span> (MutableContextWrapper) webView.getContext();</span><br><span class="line">            contextWrapper.setBaseContext(context);</span><br><span class="line">            mUsingWebViewList.add(webView);</span><br><span class="line">            <span class="keyword">return</span> webView;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="type">MutableContextWrapper</span> <span class="variable">contextWrapper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MutableContextWrapper</span>(context);</span><br><span class="line">            <span class="type">DetailWebView</span> <span class="variable">webView</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DetailWebView</span>(contextWrapper);</span><br><span class="line">            mUsingWebViewList.add(webView);</span><br><span class="line">            <span class="keyword">return</span> webView;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">recycleWebView</span><span class="params">(DetailWebView webView)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (webView == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">ViewGroup</span> <span class="variable">viewParent</span> <span class="operator">=</span> (ViewGroup) webView.getParent();</span><br><span class="line">        <span class="keyword">if</span> (viewParent != <span class="literal">null</span>) &#123;</span><br><span class="line">            viewParent.removeView(webView);</span><br><span class="line">        &#125;</span><br><span class="line">        webView.loadUrl(<span class="string">&quot;about:blank&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (mUsingWebViewList != <span class="literal">null</span> &amp;&amp; mUsingWebViewList.contains(webView)) &#123;</span><br><span class="line">            mUsingWebViewList.remove(webView);</span><br><span class="line">            <span class="type">MutableContextWrapper</span> <span class="variable">contextWrapper</span> <span class="operator">=</span> (MutableContextWrapper) webView.getContext();</span><br><span class="line">            contextWrapper.setBaseContext(App.getContext());</span><br><span class="line">            webView.setWebViewClient(<span class="literal">null</span>);</span><br><span class="line">            webView.setWebChromeClient(<span class="literal">null</span>);</span><br><span class="line">            mIdleWebViewList.add(webView);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            webView.clearHistory();</span><br><span class="line">            webView.destroy();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="预加载"><a href="#预加载" class="headerlink" title="预加载"></a>预加载</h3><p>预加载主体的实现比较简单，在信息流的RecyclerView进入IDLE状态时，判断当前有多少个条目曝光，然后启动对应数量的子线程开始下载数据为String，用户进入某个条目时，如果数据下载完毕就可以直接通过<code>WebView.loadDataWithBaseURL(mUrl, data, &quot;text/html&quot;, &quot;utf-8&quot;, null)</code>展示数据。<br> 应用对预加载下载的数据是需要统一管理的，可以考虑使用LRU缓存。不过由于是多线程下载数据，需要对在对LRU缓存读取时加锁保证线程安全，这里的锁可以选择读写锁。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;以公司的新闻资讯类产品为例，总结一下加载提速方案。&lt;br&gt;首先对于新闻资讯页面来说，不同的页面之间有很多重复的资源，这些资源可以直接保存在本地复用：将css、js和图片文件放到Assets中，应用可以拦截这部分的请求转而加载这些文件。&lt;br&gt;其次，WebView的新建与初始</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="WebView" scheme="https://adewu.github.io/tags/WebView/"/>
    
  </entry>
  
  <entry>
    <title>Android中Context的说明</title>
    <link href="https://adewu.github.io/2017/03/20/android_context/"/>
    <id>https://adewu.github.io/2017/03/20/android_context/</id>
    <published>2017-03-20T11:52:00.000Z</published>
    <updated>2024-02-11T15:00:52.665Z</updated>
    
    <content type="html"><![CDATA[<p>简单来说，Context 是一个接口，它包含有关应用程序环境的全局信息。Context 允许访问特定于应用程序的资源（例如字符串、主题、资产等）和类。同样，它处理应用程序级别的操作，如启动Activity、发送和接收广播。简而言之，Context 表示获取环境数据的句柄。</p><p>在这里你可以看到 Context 的常见用法。</p><p><img src="/../imgs/using_context.webp" alt="alt text" title="Context的用法"></p><p>同样地，下面列表中是一些 Context 的示例方法。正如我们在开始时所说的，你可以使用 Context 访问应用程序特定的资源。</p><ul><li>getAssets() 获取资源管理器 </li><li>getResources() 获取资源 </li><li>getPackageManager() 获取包管理器 </li><li>getString() 获取字符串 </li><li>getSharedPrefsFile() 获取共享首选项文件</li></ul><p>有两种主要类型的上下文。</p><p><strong>1- Application Context</strong></p><p>这种类型的上下文直接与应用的生命周期相关联。你可以通过 getApplicationContext() 方法访问它。</p><p><strong>2- Activity Context</strong> </p><p>这种类型的上下文与Activity的生命周期相关联。你可以通过 getContext() 方法访问它。</p><p>除此之外，我们还有 getBaseContext() 方法。</p><p>getBaseContext() 是 ContextWrapper 的成员方法。</p><p>ContextWrapper 是 Context 的代理实现，它简单地将所有调用委托给另一个 Context。可以通过子类化来修改行为，而不改变原始的 Context。</p><p><img src="/../imgs/context_illustrated.webp" alt="alt text" title="Context 图示"></p><p>除了这些之外，你可以看到上下文和其他类之间的关系。正如你所看到的，上下文是这个层次结构的根节点。</p><p><img src="/../imgs/context_reference.webp" alt="alt text" title="Context 图示"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;简单来说，Context 是一个接口，它包含有关应用程序环境的全局信息。Context 允许访问特定于应用程序的资源（例如字符串、主题、资产等）和类。同样，它处理应用程序级别的操作，如启动Activity、发送和接收广播。简而言之，Context 表示获取环境数据的句柄。&lt;</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="Context" scheme="https://adewu.github.io/tags/Context/"/>
    
  </entry>
  
  <entry>
    <title>Activity启动流程</title>
    <link href="https://adewu.github.io/2016/10/11/activity_star_process/"/>
    <id>https://adewu.github.io/2016/10/11/activity_star_process/</id>
    <published>2016-10-11T06:52:00.000Z</published>
    <updated>2024-02-11T12:49:54.534Z</updated>
    
    <content type="html"><![CDATA[<p>Activity是一种展示型组件，用于展示给用户一个可以交互的页面。Activity是Android中最重要的组件，对用户来说，所有的Activity就是一个App的全部，而其他组件用户是不能直接感知的。在开发层面，要启动一个Activity可以使用Intent，分显式和隐式，并且还可以设置Activity的启动模式。</p><h3 id="Activity启动的发起"><a href="#Activity启动的发起" class="headerlink" title="Activity启动的发起"></a>Activity启动的发起</h3><p>下面我们就来对Activity的工作流程进行梳理，达到对Activity整体流程的掌握。从startActivity方法开始，会走到startActivityForResult方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">startActivityForResult</span><span class="params">(<span class="meta">@RequiresPermission</span> Intent intent, <span class="type">int</span> requestCode,</span></span><br><span class="line"><span class="params"> <span class="number">2</span>            <span class="meta">@Nullable</span> Bundle options)</span> &#123;</span><br><span class="line"> <span class="number">3</span>        <span class="keyword">if</span> (mParent == <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="number">4</span>            options = transferSpringboardActivityOptions(options);</span><br><span class="line"> <span class="number">5</span>            Instrumentation.<span class="type">ActivityResult</span> <span class="variable">ar</span> <span class="operator">=</span></span><br><span class="line"> <span class="number">6</span>                mInstrumentation.execStartActivity(</span><br><span class="line"> <span class="number">7</span>                    <span class="built_in">this</span>, mMainThread.getApplicationThread(), mToken, <span class="built_in">this</span>,</span><br><span class="line"> <span class="number">8</span>                    intent, requestCode, options);</span><br><span class="line"> <span class="number">9</span>            <span class="keyword">if</span> (ar != <span class="literal">null</span>) &#123;</span><br><span class="line"><span class="number">10</span>                mMainThread.sendActivityResult(</span><br><span class="line"><span class="number">11</span>                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),</span><br><span class="line"><span class="number">12</span>                    ar.getResultData());</span><br><span class="line"><span class="number">13</span>            &#125;</span><br><span class="line"><span class="number">14</span>            <span class="keyword">if</span> (requestCode &gt;= <span class="number">0</span>) &#123;</span><br><span class="line"><span class="number">15</span>                mStartedActivity = <span class="literal">true</span>;</span><br><span class="line"><span class="number">16</span>            &#125;</span><br><span class="line"><span class="number">17</span></span><br><span class="line"><span class="number">18</span>            cancelInputsAndStartExitTransition(options);</span><br><span class="line"><span class="number">19</span>        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"><span class="number">20</span>            ...</span><br><span class="line"><span class="number">21</span>        &#125;</span><br><span class="line"><span class="number">22</span>    &#125;</span><br></pre></td></tr></table></figure><p>看到里面调用了mInstrumentation.execStartActivity方法，其中一个参数mMainThread.getApplicationThread()，它的类型是ApplicationThread，<strong>ApplicationThread是ActivityThread的内部类，继承IApplicationThread.Stub，也是个Binder对象</strong>，在Activity工作流程中有重要作用。而Instrumentation具有跟踪application及activity生命周期的功能，用于android 应用测试框架中代码检测。接着看下mInstrumentation.execStartActivity方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">public</span> ActivityResult <span class="title function_">execStartActivity</span><span class="params">(</span></span><br><span class="line"><span class="params"> <span class="number">2</span>            Context who, IBinder contextThread, IBinder token, Activity target,</span></span><br><span class="line"><span class="params"> <span class="number">3</span>            Intent intent, <span class="type">int</span> requestCode, Bundle options)</span> &#123;</span><br><span class="line"> <span class="number">4</span>        <span class="type">IApplicationThread</span> <span class="variable">whoThread</span> <span class="operator">=</span> (IApplicationThread) contextThread;</span><br><span class="line"> <span class="number">5</span>        <span class="type">Uri</span> <span class="variable">referrer</span> <span class="operator">=</span> target != <span class="literal">null</span> ? target.onProvideReferrer() : <span class="literal">null</span>;</span><br><span class="line"> <span class="number">6</span>        <span class="keyword">if</span> (referrer != <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="number">7</span>            intent.putExtra(Intent.EXTRA_REFERRER, referrer);</span><br><span class="line"> <span class="number">8</span>        &#125;</span><br><span class="line"> <span class="number">9</span>        ...</span><br><span class="line"><span class="number">10</span></span><br><span class="line"><span class="number">11</span>        <span class="keyword">try</span> &#123;</span><br><span class="line"><span class="number">12</span>            intent.migrateExtraStreamToClipData();</span><br><span class="line"><span class="number">13</span>            intent.prepareToLeaveProcess(who);</span><br><span class="line"><span class="number">14</span>            <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> ActivityManager.getService()</span><br><span class="line"><span class="number">15</span>                .startActivity(whoThread, who.getBasePackageName(), intent,</span><br><span class="line"><span class="number">16</span>                        intent.resolveTypeIfNeeded(who.getContentResolver()),</span><br><span class="line"><span class="number">17</span>                        token, target != <span class="literal">null</span> ? target.mEmbeddedID : <span class="literal">null</span>,</span><br><span class="line"><span class="number">18</span>                        requestCode, <span class="number">0</span>, <span class="literal">null</span>, options);</span><br><span class="line"><span class="number">19</span>            checkStartActivityResult(result, intent);</span><br><span class="line"><span class="number">20</span>        &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line"><span class="number">21</span>            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;Failure from system&quot;</span>, e);</span><br><span class="line"><span class="number">22</span>        &#125;</span><br><span class="line"><span class="number">23</span>        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"><span class="number">24</span>    &#125;</span><br></pre></td></tr></table></figure><p>这里看到Activity的启动又交给了ActivityManager.getService()</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">public</span> <span class="keyword">static</span> IActivityManager <span class="title function_">getService</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="number">2</span>        <span class="keyword">return</span> IActivityManagerSingleton.get();</span><br><span class="line"> <span class="number">3</span>    &#125;</span><br><span class="line"> <span class="number">4</span>    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Singleton&lt;IActivityManager&gt; IActivityManagerSingleton =</span><br><span class="line"> <span class="number">5</span>            <span class="keyword">new</span> <span class="title class_">Singleton</span>&lt;IActivityManager&gt;() &#123;</span><br><span class="line"> <span class="number">6</span>                <span class="meta">@Override</span></span><br><span class="line"> <span class="number">7</span>                <span class="keyword">protected</span> IActivityManager <span class="title function_">create</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="number">8</span>                    <span class="keyword">final</span> <span class="type">IBinder</span> <span class="variable">b</span> <span class="operator">=</span> ServiceManager.getService(Context.ACTIVITY_SERVICE);</span><br><span class="line"> <span class="number">9</span>                    <span class="keyword">final</span> <span class="type">IActivityManager</span> <span class="variable">am</span> <span class="operator">=</span> IActivityManager.Stub.asInterface(b);</span><br><span class="line"><span class="number">10</span>                    <span class="keyword">return</span> am;</span><br><span class="line"><span class="number">11</span>                &#125;</span><br><span class="line"><span class="number">12</span>            &#125;;</span><br></pre></td></tr></table></figure><p>看到这里你应该明白了：这里是获取一个跨进程的服务。然后我们看下著名的ActivityManagerService类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>    <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ActivityManagerService</span> <span class="keyword">extends</span> <span class="title class_">IActivityManager</span>.Stub</span><br><span class="line"><span class="number">2</span>        <span class="keyword">implements</span> <span class="title class_">Watchdog</span>.Monitor, BatteryStatsImpl.BatteryCallback</span><br><span class="line"><span class="number">3</span></span><br></pre></td></tr></table></figure><p>ActivityManagerService继承IActivityManager.Stub，实际也就是继承了Binder并且实现了IActivityManager这个Binder接口，AMS也就是一个<a href="https://cloud.tencent.com/developer/tools/blog-entry?target=http://mp.weixin.qq.com/s?__biz=MzU2NjgwNjc0NQ==&mid=2247483688&idx=1&sn=b141672a67b2d522e0597d8fccb1c294&chksm=fca7909ecbd019887a3a3cc8fdc96a3c5eb9d8fe72ca6650d753bb6dde1f40e0877faea7f3d7&scene=21%23wechat_redirect&source=article&objectId=1666801">Binder</a>，是IActivityManager的集体实现。所以ActivityManager.getService()获取的Binder对象，具体实现是ActivityManagerService（AMS），并且是通过单例提供服务的。</p><p>然后ActivityManager.getService().startActivity有个返回值result，且调用了checkStartActivityResult(result, intent)：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">checkStartActivityResult</span><span class="params">(<span class="type">int</span> res, Object intent)</span> &#123;</span><br><span class="line"> <span class="number">2</span>        <span class="keyword">if</span> (!ActivityManager.isStartResultFatalError(res)) &#123;</span><br><span class="line"> <span class="number">3</span>            <span class="keyword">return</span>;</span><br><span class="line"> <span class="number">4</span>        &#125;</span><br><span class="line"> <span class="number">5</span></span><br><span class="line"> <span class="number">6</span>        <span class="keyword">switch</span> (res) &#123;</span><br><span class="line"> <span class="number">7</span>            <span class="keyword">case</span> ActivityManager.START_INTENT_NOT_RESOLVED:</span><br><span class="line"> <span class="number">8</span>            <span class="keyword">case</span> ActivityManager.START_CLASS_NOT_FOUND:</span><br><span class="line"> <span class="number">9</span>                <span class="keyword">if</span> (intent <span class="keyword">instanceof</span> Intent &amp;&amp; ((Intent)intent).getComponent() != <span class="literal">null</span>)</span><br><span class="line"><span class="number">10</span>                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ActivityNotFoundException</span>(</span><br><span class="line"><span class="number">11</span>                            <span class="string">&quot;Unable to find explicit activity class &quot;</span></span><br><span class="line"><span class="number">12</span>                            + ((Intent)intent).getComponent().toShortString()</span><br><span class="line"><span class="number">13</span>                            + <span class="string">&quot;; have you declared this activity in your AndroidManifest.xml?&quot;</span>);</span><br><span class="line"><span class="number">14</span>                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ActivityNotFoundException</span>(</span><br><span class="line"><span class="number">15</span>                        <span class="string">&quot;No Activity found to handle &quot;</span> + intent);</span><br><span class="line"><span class="number">16</span>            <span class="keyword">case</span> ActivityManager.START_PERMISSION_DENIED:</span><br><span class="line"><span class="number">17</span>                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">SecurityException</span>(<span class="string">&quot;Not allowed to start activity &quot;</span></span><br><span class="line"><span class="number">18</span>                        + intent);</span><br><span class="line"><span class="number">19</span>            ...</span><br><span class="line"><span class="number">20</span></span><br><span class="line"><span class="number">21</span>            <span class="keyword">case</span> ActivityManager.START_CANCELED:</span><br><span class="line"><span class="number">22</span>                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">AndroidRuntimeException</span>(<span class="string">&quot;Activity could not be started for &quot;</span></span><br><span class="line"><span class="number">23</span>                        + intent);</span><br><span class="line"><span class="number">24</span>            <span class="keyword">default</span>:</span><br><span class="line"><span class="number">25</span>                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">AndroidRuntimeException</span>(<span class="string">&quot;Unknown error code &quot;</span></span><br><span class="line"><span class="number">26</span>                        + res + <span class="string">&quot; when starting &quot;</span> + intent);</span><br><span class="line"><span class="number">27</span>        &#125;</span><br><span class="line"><span class="number">28</span>    &#125;</span><br></pre></td></tr></table></figure><p>这是用来检查Activity启动的结果，如果发生致命错误，就会抛出对应的异常。看到第一个case中就抛出了 have you declared this activity in your AndroidManifest.xml?——如果Activity没在Manifest中注册就会有这个错误。</p><h3 id="Activity的管理-AMS"><a href="#Activity的管理-AMS" class="headerlink" title="Activity的管理-AMS"></a>Activity的管理-AMS</h3><p>好了，到这里，Activity的启动就转移到系统进程提供的服务AMS中了，接着看AMS的startActivity：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>    <span class="meta">@Override</span></span><br><span class="line"><span class="number">2</span>    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">startActivity</span><span class="params">(IApplicationThread caller, String callingPackage,</span></span><br><span class="line"><span class="params"><span class="number">3</span>            Intent intent, String resolvedType, IBinder resultTo, String resultWho, <span class="type">int</span> requestCode,</span></span><br><span class="line"><span class="params"><span class="number">4</span>            <span class="type">int</span> startFlags, ProfilerInfo profilerInfo, Bundle bOptions)</span> &#123;</span><br><span class="line"><span class="number">5</span>        <span class="keyword">return</span> mActivityTaskManager.startActivity(caller, callingPackage, intent, resolvedType,</span><br><span class="line"><span class="number">6</span>                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);</span><br><span class="line"><span class="number">7</span>    &#125;</span><br></pre></td></tr></table></figure><p>AMS把启动转移到了ActivityTaskManagerService（ATMS）中了，ATMS用于管理Activity及其<a href="https://cloud.tencent.com/product/tke?from_column=20065&from=20065">容器</a>（任务、堆栈、显示等）的系统服务。接着看：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span><span class="comment">//ActivityTaskManagerService</span></span><br><span class="line"> <span class="number">2</span>    <span class="meta">@Override</span></span><br><span class="line"> <span class="number">3</span>    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">startActivity</span><span class="params">(IApplicationThread caller, String callingPackage,</span></span><br><span class="line"><span class="params"> <span class="number">4</span>            Intent intent, String resolvedType, IBinder resultTo, String resultWho, <span class="type">int</span> requestCode,</span></span><br><span class="line"><span class="params"> <span class="number">5</span>            <span class="type">int</span> startFlags, ProfilerInfo profilerInfo, Bundle bOptions)</span> &#123;</span><br><span class="line"> <span class="number">6</span>        <span class="keyword">return</span> startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,</span><br><span class="line"> <span class="number">7</span>                resultWho, requestCode, startFlags, profilerInfo, bOptions,</span><br><span class="line"> <span class="number">8</span>                UserHandle.getCallingUserId());</span><br><span class="line"> <span class="number">9</span>    &#125;</span><br><span class="line"><span class="number">10</span></span><br><span class="line"><span class="number">11</span>    <span class="meta">@Override</span></span><br><span class="line"><span class="number">12</span>    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">startActivityAsUser</span><span class="params">(IApplicationThread caller, String callingPackage,</span></span><br><span class="line"><span class="params"><span class="number">13</span>            Intent intent, String resolvedType, IBinder resultTo, String resultWho, <span class="type">int</span> requestCode,</span></span><br><span class="line"><span class="params"><span class="number">14</span>            <span class="type">int</span> startFlags, ProfilerInfo profilerInfo, Bundle bOptions, <span class="type">int</span> userId)</span> &#123;</span><br><span class="line"><span class="number">15</span>        <span class="keyword">return</span> startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,</span><br><span class="line"><span class="number">16</span>                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,</span><br><span class="line"><span class="number">17</span>                <span class="literal">true</span> <span class="comment">/*validateIncomingUser*/</span>);</span><br><span class="line"><span class="number">18</span>    &#125;</span><br><span class="line"><span class="number">19</span></span><br><span class="line"><span class="number">20</span>    <span class="type">int</span> <span class="title function_">startActivityAsUser</span><span class="params">(IApplicationThread caller, String callingPackage,</span></span><br><span class="line"><span class="params"><span class="number">21</span>            Intent intent, String resolvedType, IBinder resultTo, String resultWho, <span class="type">int</span> requestCode,</span></span><br><span class="line"><span class="params"><span class="number">22</span>            <span class="type">int</span> startFlags, ProfilerInfo profilerInfo, Bundle bOptions, <span class="type">int</span> userId,</span></span><br><span class="line"><span class="params"><span class="number">23</span>            <span class="type">boolean</span> validateIncomingUser)</span> &#123;</span><br><span class="line"><span class="number">24</span>        enforceNotIsolatedCaller(<span class="string">&quot;startActivityAsUser&quot;</span>);</span><br><span class="line"><span class="number">25</span></span><br><span class="line"><span class="number">26</span>        userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,</span><br><span class="line"><span class="number">27</span>                Binder.getCallingPid(), Binder.getCallingUid(), <span class="string">&quot;startActivityAsUser&quot;</span>);</span><br><span class="line"><span class="number">28</span></span><br><span class="line"><span class="number">29</span>        <span class="comment">// <span class="doctag">TODO:</span> Switch to user app stacks here.</span></span><br><span class="line"><span class="number">30</span>        <span class="keyword">return</span> getActivityStartController().obtainStarter(intent, <span class="string">&quot;startActivityAsUser&quot;</span>)</span><br><span class="line"><span class="number">31</span>                .setCaller(caller)</span><br><span class="line"><span class="number">32</span>                .setCallingPackage(callingPackage)</span><br><span class="line"><span class="number">33</span>                .setResolvedType(resolvedType)</span><br><span class="line"><span class="number">34</span>                .setResultTo(resultTo)</span><br><span class="line"><span class="number">35</span>                .setResultWho(resultWho)</span><br><span class="line"><span class="number">36</span>                .setRequestCode(requestCode)</span><br><span class="line"><span class="number">37</span>                .setStartFlags(startFlags)</span><br><span class="line"><span class="number">38</span>                .setProfilerInfo(profilerInfo)</span><br><span class="line"><span class="number">39</span>                .setActivityOptions(bOptions)</span><br><span class="line"><span class="number">40</span>                .setMayWait(userId)</span><br><span class="line"><span class="number">41</span>                .execute();</span><br><span class="line"><span class="number">42</span></span><br><span class="line"><span class="number">43</span>    &#125;</span><br></pre></td></tr></table></figure><p>跟到startActivityAsUser中，通过getActivityStartController().obtainStarter方法获取ActivityStarter实例 然后调用一系列方法，最后的execute()方法是开始启动activity：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="type">int</span> <span class="title function_">execute</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="number">2</span>        <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="number">3</span>            <span class="comment">// TODO(b/64750076): Look into passing request directly to these methods to allow</span></span><br><span class="line"> <span class="number">4</span>            <span class="comment">// for transactional diffs and preprocessing.</span></span><br><span class="line"> <span class="number">5</span>            <span class="keyword">if</span> (mRequest.mayWait) &#123;</span><br><span class="line"> <span class="number">6</span>                <span class="keyword">return</span> startActivityMayWait(mRequest.caller, mRequest.callingUid,</span><br><span class="line"> <span class="number">7</span>                        mRequest.callingPackage, mRequest.realCallingPid, mRequest.realCallingUid,</span><br><span class="line"> <span class="number">8</span>                        mRequest.intent, mRequest.resolvedType,</span><br><span class="line"> <span class="number">9</span>                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,</span><br><span class="line"><span class="number">10</span>                        mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,</span><br><span class="line"><span class="number">11</span>                        mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,</span><br><span class="line"><span class="number">12</span>                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,</span><br><span class="line"><span class="number">13</span>                        mRequest.inTask, mRequest.reason,</span><br><span class="line"><span class="number">14</span>                        mRequest.allowPendingRemoteAnimationRegistryLookup,</span><br><span class="line"><span class="number">15</span>                        mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);</span><br><span class="line"><span class="number">16</span>            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"><span class="number">17</span>                <span class="keyword">return</span> startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,</span><br><span class="line"><span class="number">18</span>                        mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,</span><br><span class="line"><span class="number">19</span>                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,</span><br><span class="line"><span class="number">20</span>                        mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,</span><br><span class="line"><span class="number">21</span>                        mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,</span><br><span class="line"><span class="number">22</span>                        mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,</span><br><span class="line"><span class="number">23</span>                        mRequest.ignoreTargetSecurity, mRequest.componentSpecified,</span><br><span class="line"><span class="number">24</span>                        mRequest.outActivity, mRequest.inTask, mRequest.reason,</span><br><span class="line"><span class="number">25</span>                        mRequest.allowPendingRemoteAnimationRegistryLookup,</span><br><span class="line"><span class="number">26</span>                        mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);</span><br><span class="line"><span class="number">27</span>            &#125;</span><br><span class="line"><span class="number">28</span>        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line"><span class="number">29</span>            onExecutionComplete();</span><br><span class="line"><span class="number">30</span>        &#125;</span><br><span class="line"><span class="number">31</span>    &#125;</span><br></pre></td></tr></table></figure><p>分了两种情况，不过 不论startActivityMayWait还是startActivity最终都是走到下面这个startActivity方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">private</span> <span class="type">int</span> <span class="title function_">startActivity</span><span class="params">(<span class="keyword">final</span> ActivityRecord r, ActivityRecord sourceRecord,</span></span><br><span class="line"><span class="params"> <span class="number">2</span>                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,</span></span><br><span class="line"><span class="params"> <span class="number">3</span>                <span class="type">int</span> startFlags, <span class="type">boolean</span> doResume, ActivityOptions options, TaskRecord inTask,</span></span><br><span class="line"><span class="params"> <span class="number">4</span>                ActivityRecord[] outActivity, <span class="type">boolean</span> restrictedBgActivity)</span> &#123;</span><br><span class="line"> <span class="number">5</span>        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> START_CANCELED;</span><br><span class="line"> <span class="number">6</span>        <span class="keyword">final</span> ActivityStack startedActivityStack;</span><br><span class="line"> <span class="number">7</span>        <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="number">8</span>            mService.mWindowManager.deferSurfaceLayout();</span><br><span class="line"> <span class="number">9</span>            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,</span><br><span class="line"><span class="number">10</span>                    startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);</span><br><span class="line"><span class="number">11</span>        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line"><span class="number">12</span>            <span class="keyword">final</span> <span class="type">ActivityStack</span> <span class="variable">currentStack</span> <span class="operator">=</span> r.getActivityStack();</span><br><span class="line"><span class="number">13</span>            startedActivityStack = currentStack != <span class="literal">null</span> ? currentStack : mTargetStack;</span><br><span class="line"><span class="number">14</span></span><br><span class="line"><span class="number">15</span>           ...</span><br><span class="line"><span class="number">16</span>        &#125;</span><br><span class="line"><span class="number">17</span></span><br><span class="line"><span class="number">18</span>        postStartActivityProcessing(r, result, startedActivityStack);</span><br><span class="line"><span class="number">19</span>        <span class="keyword">return</span> result;</span><br><span class="line"><span class="number">20</span>    &#125;</span><br></pre></td></tr></table></figure><p>里面有调用了startActivityUnchecked方法，startActivityUnchecked内部调用了ActivityStack的startActivityLocked方法，startActivityLocked内部调用ensureActivitiesVisibleLocked方法，ensureActivitiesVisibleLocked又调用makeVisibleAndRestartIfNeeded方法，来看下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">makeVisibleAndRestartIfNeeded</span><span class="params">(ActivityRecord starting, <span class="type">int</span> configChanges,</span></span><br><span class="line"><span class="params"> <span class="number">2</span>            <span class="type">boolean</span> isTop, <span class="type">boolean</span> andResume, ActivityRecord r)</span> &#123;</span><br><span class="line"> <span class="number">3</span>        <span class="comment">// We need to make sure the app is running if it&#x27;s the top, or it is just made visible from</span></span><br><span class="line"> <span class="number">4</span>        <span class="comment">// invisible. If the app is already visible, it must have died while it was visible. In this</span></span><br><span class="line"> <span class="number">5</span>        <span class="comment">// case, we&#x27;ll show the dead window but will not restart the app. Otherwise we could end up</span></span><br><span class="line"> <span class="number">6</span>        <span class="comment">// thrashing.</span></span><br><span class="line"> <span class="number">7</span>        <span class="keyword">if</span> (isTop || !r.visible) &#123;</span><br><span class="line"> <span class="number">8</span>            <span class="comment">// This activity needs to be visible, but isn&#x27;t even running...</span></span><br><span class="line"> <span class="number">9</span>            <span class="comment">// get it started and resume if no other stack in this stack is resumed.</span></span><br><span class="line"><span class="number">10</span>            <span class="keyword">if</span> (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, <span class="string">&quot;Start and freeze screen for &quot;</span> + r);</span><br><span class="line"><span class="number">11</span>            <span class="keyword">if</span> (r != starting) &#123;</span><br><span class="line"><span class="number">12</span>                r.startFreezingScreenLocked(r.app, configChanges);</span><br><span class="line"><span class="number">13</span>            &#125;</span><br><span class="line"><span class="number">14</span>            <span class="keyword">if</span> (!r.visible || r.mLaunchTaskBehind) &#123;</span><br><span class="line"><span class="number">15</span>                <span class="keyword">if</span> (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, <span class="string">&quot;Starting and making visible: &quot;</span> + r);</span><br><span class="line"><span class="number">16</span>                r.setVisible(<span class="literal">true</span>);</span><br><span class="line"><span class="number">17</span>            &#125;</span><br><span class="line"><span class="number">18</span>            <span class="keyword">if</span> (r != starting) &#123;</span><br><span class="line"><span class="number">19</span>                <span class="comment">// We should not resume activities that being launched behind because these</span></span><br><span class="line"><span class="number">20</span>                <span class="comment">// activities are actually behind other fullscreen activities, but still required</span></span><br><span class="line"><span class="number">21</span>                <span class="comment">// to be visible (such as performing Recents animation).</span></span><br><span class="line"><span class="number">22</span>                mStackSupervisor.startSpecificActivityLocked(r, andResume &amp;&amp; !r.mLaunchTaskBehind,</span><br><span class="line"><span class="number">23</span>                        <span class="literal">true</span> <span class="comment">/* checkConfig */</span>);</span><br><span class="line"><span class="number">24</span>                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"><span class="number">25</span>            &#125;</span><br><span class="line"><span class="number">26</span>        &#125;</span><br><span class="line"><span class="number">27</span>        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"><span class="number">28</span>    &#125;</span><br></pre></td></tr></table></figure><p>看到最后调用了ActivityStackSupervisor的startSpecificActivityLocked方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">void</span> <span class="title function_">startSpecificActivityLocked</span><span class="params">(ActivityRecord r, <span class="type">boolean</span> andResume, <span class="type">boolean</span> checkConfig)</span> &#123;</span><br><span class="line"> <span class="number">2</span>        <span class="comment">// Is this activity&#x27;s application already running?</span></span><br><span class="line"> <span class="number">3</span>        <span class="keyword">final</span> <span class="type">WindowProcessController</span> <span class="variable">wpc</span> <span class="operator">=</span></span><br><span class="line"> <span class="number">4</span>                mService.getProcessController(r.processName, r.info.applicationInfo.uid);</span><br><span class="line"> <span class="number">5</span></span><br><span class="line"> <span class="number">6</span>        <span class="type">boolean</span> <span class="variable">knownToBeDead</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="number">7</span>        <span class="keyword">if</span> (wpc != <span class="literal">null</span> &amp;&amp; wpc.hasThread()) &#123;</span><br><span class="line"> <span class="number">8</span>            <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="number">9</span>                realStartActivityLocked(r, wpc, andResume, checkConfig);</span><br><span class="line"><span class="number">10</span>                <span class="keyword">return</span>;</span><br><span class="line"><span class="number">11</span>            &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line"><span class="number">12</span>                Slog.w(TAG, <span class="string">&quot;Exception when starting activity &quot;</span></span><br><span class="line"><span class="number">13</span>                        + r.intent.getComponent().flattenToShortString(), e);</span><br><span class="line"><span class="number">14</span>            &#125;</span><br><span class="line"><span class="number">15</span></span><br><span class="line"><span class="number">16</span>            <span class="comment">// If a dead object exception was thrown -- fall through to</span></span><br><span class="line"><span class="number">17</span>            <span class="comment">// restart the application.</span></span><br><span class="line"><span class="number">18</span>            knownToBeDead = <span class="literal">true</span>;</span><br><span class="line"><span class="number">19</span>        &#125;</span><br><span class="line"><span class="number">20</span></span><br><span class="line"><span class="number">21</span>        ...</span><br><span class="line"><span class="number">22</span>    &#125;</span><br></pre></td></tr></table></figure><p>接着调用了realStartActivityLocked方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="type">boolean</span> <span class="title function_">realStartActivityLocked</span><span class="params">(ActivityRecord r, WindowProcessController proc,</span></span><br><span class="line"><span class="params"> <span class="number">2</span>            <span class="type">boolean</span> andResume, <span class="type">boolean</span> checkConfig)</span> <span class="keyword">throws</span> RemoteException &#123;</span><br><span class="line"> <span class="number">3</span></span><br><span class="line"> <span class="number">4</span>            ...</span><br><span class="line"> <span class="number">5</span></span><br><span class="line"> <span class="number">6</span>                <span class="comment">// Create activity launch transaction.</span></span><br><span class="line"> <span class="number">7</span>                <span class="keyword">final</span> <span class="type">ClientTransaction</span> <span class="variable">clientTransaction</span> <span class="operator">=</span> ClientTransaction.obtain(</span><br><span class="line"> <span class="number">8</span>                        proc.getThread(), r.appToken);</span><br><span class="line"> <span class="number">9</span></span><br><span class="line"><span class="number">10</span>                <span class="keyword">final</span> <span class="type">DisplayContent</span> <span class="variable">dc</span> <span class="operator">=</span> r.getDisplay().mDisplayContent;</span><br><span class="line"><span class="number">11</span>                clientTransaction.addCallback(LaunchActivityItem.obtain(<span class="keyword">new</span> <span class="title class_">Intent</span>(r.intent),</span><br><span class="line"><span class="number">12</span>                        System.identityHashCode(r), r.info,</span><br><span class="line"><span class="number">13</span>                        <span class="comment">// <span class="doctag">TODO:</span> Have this take the merged configuration instead of separate global</span></span><br><span class="line"><span class="number">14</span>                        <span class="comment">// and override configs.</span></span><br><span class="line"><span class="number">15</span>                        mergedConfiguration.getGlobalConfiguration(),</span><br><span class="line"><span class="number">16</span>                        mergedConfiguration.getOverrideConfiguration(), r.compat,</span><br><span class="line"><span class="number">17</span>                        r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),</span><br><span class="line"><span class="number">18</span>                        r.icicle, r.persistentState, results, newIntents,</span><br><span class="line"><span class="number">19</span>                        dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),</span><br><span class="line"><span class="number">20</span>                                r.assistToken));</span><br><span class="line"><span class="number">21</span></span><br><span class="line"><span class="number">22</span>                <span class="comment">// Set desired final state.</span></span><br><span class="line"><span class="number">23</span>                <span class="keyword">final</span> ActivityLifecycleItem lifecycleItem;</span><br><span class="line"><span class="number">24</span>                <span class="keyword">if</span> (andResume) &#123;</span><br><span class="line"><span class="number">25</span>                    lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());</span><br><span class="line"><span class="number">26</span>                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"><span class="number">27</span>                    lifecycleItem = PauseActivityItem.obtain();</span><br><span class="line"><span class="number">28</span>                &#125;</span><br><span class="line"><span class="number">29</span>                clientTransaction.setLifecycleStateRequest(lifecycleItem);</span><br><span class="line"><span class="number">30</span></span><br><span class="line"><span class="number">31</span>                <span class="comment">// Schedule transaction.</span></span><br><span class="line"><span class="number">32</span>                mService.getLifecycleManager().scheduleTransaction(clientTransaction);</span><br><span class="line"><span class="number">33</span></span><br><span class="line"><span class="number">34</span>                ...</span><br><span class="line"><span class="number">35</span></span><br><span class="line"><span class="number">36</span>        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"><span class="number">37</span>    &#125;</span><br></pre></td></tr></table></figure><p>中间有段代码如上，通过 ClientTransaction.obtain( proc.getThread(), r.appToken)获取了clientTransaction，其中参数proc.getThread()是IApplicationThread，就是前面提到的ApplicationThread在系统进程的代理。</p><p><strong>ClientTransaction</strong>是包含一系列的message的容器，message用于 <strong>发送到客户端</strong>，包含回调方法和生命周期状态。</p><p>接着看，使用clientTransaction.addCallback添加了LaunchActivityItem实例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>    <span class="comment">//都是用来发送到客户端的</span></span><br><span class="line"><span class="number">2</span>    <span class="keyword">private</span> List&lt;ClientTransactionItem&gt; mActivityCallbacks;</span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="number">4</span>    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addCallback</span><span class="params">(ClientTransactionItem activityCallback)</span> &#123;</span><br><span class="line"><span class="number">5</span>        <span class="keyword">if</span> (mActivityCallbacks == <span class="literal">null</span>) &#123;</span><br><span class="line"><span class="number">6</span>            mActivityCallbacks = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"><span class="number">7</span>        &#125;</span><br><span class="line"><span class="number">8</span>        mActivityCallbacks.add(activityCallback);</span><br><span class="line"><span class="number">9</span>    &#125;</span><br></pre></td></tr></table></figure><p>看下LaunchActivityItem实例的获取：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="comment">/** Obtain an instance initialized with provided params. */</span></span><br><span class="line"> <span class="number">2</span>    <span class="keyword">public</span> <span class="keyword">static</span> LaunchActivityItem <span class="title function_">obtain</span><span class="params">(Intent intent, <span class="type">int</span> ident, ActivityInfo info,</span></span><br><span class="line"><span class="params"> <span class="number">3</span>            Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo,</span></span><br><span class="line"><span class="params"> <span class="number">4</span>            String referrer, IVoiceInteractor voiceInteractor, <span class="type">int</span> procState, Bundle state,</span></span><br><span class="line"><span class="params"> <span class="number">5</span>            PersistableBundle persistentState, List&lt;ResultInfo&gt; pendingResults,</span></span><br><span class="line"><span class="params"> <span class="number">6</span>            List&lt;ReferrerIntent&gt; pendingNewIntents, <span class="type">boolean</span> isForward, ProfilerInfo profilerInfo,</span></span><br><span class="line"><span class="params"> <span class="number">7</span>            IBinder assistToken)</span> &#123;</span><br><span class="line"> <span class="number">8</span>        <span class="type">LaunchActivityItem</span> <span class="variable">instance</span> <span class="operator">=</span> ObjectPool.obtain(LaunchActivityItem.class);</span><br><span class="line"> <span class="number">9</span>        <span class="keyword">if</span> (instance == <span class="literal">null</span>) &#123;</span><br><span class="line"><span class="number">10</span>            instance = <span class="keyword">new</span> <span class="title class_">LaunchActivityItem</span>();</span><br><span class="line"><span class="number">11</span>        &#125;</span><br><span class="line"><span class="number">12</span>        setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer,</span><br><span class="line"><span class="number">13</span>                voiceInteractor, procState, state, persistentState, pendingResults,</span><br><span class="line"><span class="number">14</span>                pendingNewIntents, isForward, profilerInfo, assistToken);</span><br><span class="line"><span class="number">15</span></span><br><span class="line"><span class="number">16</span>        <span class="keyword">return</span> instance;</span><br><span class="line"><span class="number">17</span>    &#125;</span><br></pre></td></tr></table></figure><p>new了一个LaunchActivityItem然后设置各种值。我们从名字就能看出，它就是用来启动activity的。它是怎么发挥作用的呢？接着看：</p><p>回到realStartActivityLocked方法，接着调用了mService.getLifecycleManager().scheduleTransaction(clientTransaction)，mService是前面提到了ActivityTaskManagerService，getLifecycleManager()方法获取的是ClientLifecycleManager实例，它的scheduleTransaction方法如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>    <span class="keyword">void</span> <span class="title function_">scheduleTransaction</span><span class="params">(ClientTransaction transaction)</span> <span class="keyword">throws</span> RemoteException &#123;</span><br><span class="line"><span class="number">2</span>        <span class="keyword">final</span> <span class="type">IApplicationThread</span> <span class="variable">client</span> <span class="operator">=</span> transaction.getClient();</span><br><span class="line"><span class="number">3</span>        transaction.schedule();</span><br><span class="line"><span class="number">4</span>        <span class="keyword">if</span> (!(client <span class="keyword">instanceof</span> Binder)) &#123;</span><br><span class="line"><span class="number">5</span>            transaction.recycle();</span><br><span class="line"><span class="number">6</span>        &#125;</span><br><span class="line"><span class="number">7</span>    &#125;</span><br></pre></td></tr></table></figure><p>就是调用ClientTransaction的schedule方法，那就看看：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">schedule</span><span class="params">()</span> <span class="keyword">throws</span> RemoteException &#123;</span><br><span class="line"><span class="number">2</span>        mClient.scheduleTransaction(<span class="built_in">this</span>);</span><br><span class="line"><span class="number">3</span>    &#125;</span><br></pre></td></tr></table></figure><p>很简单，就是调用IApplicationThread的scheduleTransaction方法。由于IApplicationThread是ApplicationThread在系统进程的代理，所以真正执行的地方就是 客户端的ApplicationThread中了。也就是说，<strong>Activity启动的操作又跨进程的还给了客户端</strong>。</p><p>好了，到这里我们稍稍梳理下：启动Activity的操作从客户端 跨进程 转移到 AMS，AMS通过ActivityTaskManagerService、ActivityStarter、ActivityStack、ActivityStackSupervisor 对 Activity任务、activity栈、Activity记录 管理后，又用过跨进程把正在启动过程又转移到了客户端。</p><h3 id="线程切换及消息处理—mH"><a href="#线程切换及消息处理—mH" class="headerlink" title="线程切换及消息处理—mH"></a>线程切换及消息处理—mH</h3><p>接着上面的分析，我们找到ApplicationThread的scheduleTransaction方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>        <span class="meta">@Override</span></span><br><span class="line"><span class="number">2</span>        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">scheduleTransaction</span><span class="params">(ClientTransaction transaction)</span> <span class="keyword">throws</span> RemoteException &#123;</span><br><span class="line"><span class="number">3</span>            ActivityThread.<span class="built_in">this</span>.scheduleTransaction(transaction);</span><br><span class="line"><span class="number">4</span>        &#125;</span><br></pre></td></tr></table></figure><p>那就再看ActivityThread的scheduleTransaction方法，实际在其父类ClientTransactionHandler中：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>    <span class="keyword">void</span> <span class="title function_">scheduleTransaction</span><span class="params">(ClientTransaction transaction)</span> &#123;</span><br><span class="line"><span class="number">2</span>        transaction.preExecute(<span class="built_in">this</span>);</span><br><span class="line"><span class="number">3</span>        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);</span><br><span class="line"><span class="number">4</span>    &#125;</span><br></pre></td></tr></table></figure><p>使用sendMessage发送消息，参数是ActivityThread.H.EXECUTE_TRANSACTION和transaction，接着看：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">(<span class="type">int</span> what, Object obj)</span> &#123;</span><br><span class="line"> <span class="number">2</span>        sendMessage(what, obj, <span class="number">0</span>, <span class="number">0</span>, <span class="literal">false</span>);</span><br><span class="line"> <span class="number">3</span>    &#125;</span><br><span class="line"> <span class="number">4</span>    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">(<span class="type">int</span> what, Object obj, <span class="type">int</span> arg1, <span class="type">int</span> arg2, <span class="type">boolean</span> async)</span> &#123;</span><br><span class="line"> <span class="number">5</span>        <span class="keyword">if</span> (DEBUG_MESSAGES) &#123;</span><br><span class="line"> <span class="number">6</span>            Slog.v(TAG,</span><br><span class="line"> <span class="number">7</span>                    <span class="string">&quot;SCHEDULE &quot;</span> + what + <span class="string">&quot; &quot;</span> + mH.codeToString(what) + <span class="string">&quot;: &quot;</span> + arg1 + <span class="string">&quot; / &quot;</span> + obj);</span><br><span class="line"> <span class="number">8</span>        &#125;</span><br><span class="line"> <span class="number">9</span>        <span class="type">Message</span> <span class="variable">msg</span> <span class="operator">=</span> Message.obtain();</span><br><span class="line"><span class="number">10</span>        msg.what = what;</span><br><span class="line"><span class="number">11</span>        msg.obj = obj;</span><br><span class="line"><span class="number">12</span>        msg.arg1 = arg1;</span><br><span class="line"><span class="number">13</span>        msg.arg2 = arg2;</span><br><span class="line"><span class="number">14</span>        <span class="keyword">if</span> (async) &#123;</span><br><span class="line"><span class="number">15</span>            msg.setAsynchronous(<span class="literal">true</span>);</span><br><span class="line"><span class="number">16</span>        &#125;</span><br><span class="line"><span class="number">17</span>        mH.sendMessage(msg);</span><br><span class="line"><span class="number">18</span>    &#125;</span><br></pre></td></tr></table></figure><p>最后调用了mH.sendMessage(msg)，mH是个啥？我们看看：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br></pre></td><td class="code"><pre><span class="line">  <span class="number">1</span><span class="comment">//ActivityThread</span></span><br><span class="line">  2final <span class="type">H</span> <span class="variable">mH</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">H</span>();</span><br><span class="line">  <span class="number">3</span></span><br><span class="line">  <span class="number">4</span>    <span class="keyword">class</span> <span class="title class_">H</span> <span class="keyword">extends</span> <span class="title class_">Handler</span> &#123;</span><br><span class="line">  <span class="number">5</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">BIND_APPLICATION</span>        <span class="operator">=</span> <span class="number">110</span>;</span><br><span class="line">  <span class="number">6</span>        <span class="meta">@UnsupportedAppUsage</span></span><br><span class="line">  <span class="number">7</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">EXIT_APPLICATION</span>        <span class="operator">=</span> <span class="number">111</span>;</span><br><span class="line">  <span class="number">8</span>        <span class="meta">@UnsupportedAppUsage</span></span><br><span class="line">  <span class="number">9</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">RECEIVER</span>                <span class="operator">=</span> <span class="number">113</span>;</span><br><span class="line"> <span class="number">10</span>        <span class="meta">@UnsupportedAppUsage</span></span><br><span class="line"> <span class="number">11</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">CREATE_SERVICE</span>          <span class="operator">=</span> <span class="number">114</span>;</span><br><span class="line"> <span class="number">12</span>        <span class="meta">@UnsupportedAppUsage</span></span><br><span class="line"> <span class="number">13</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">SERVICE_ARGS</span>            <span class="operator">=</span> <span class="number">115</span>;</span><br><span class="line"> <span class="number">14</span>        <span class="meta">@UnsupportedAppUsage</span></span><br><span class="line"> <span class="number">15</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">STOP_SERVICE</span>            <span class="operator">=</span> <span class="number">116</span>;</span><br><span class="line"> <span class="number">16</span></span><br><span class="line"> <span class="number">17</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">CONFIGURATION_CHANGED</span>   <span class="operator">=</span> <span class="number">118</span>;</span><br><span class="line"> <span class="number">18</span>        ...</span><br><span class="line"> <span class="number">19</span>        <span class="meta">@UnsupportedAppUsage</span></span><br><span class="line"> <span class="number">20</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">BIND_SERVICE</span>            <span class="operator">=</span> <span class="number">121</span>;</span><br><span class="line"> <span class="number">21</span>        <span class="meta">@UnsupportedAppUsage</span></span><br><span class="line"> <span class="number">22</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">UNBIND_SERVICE</span>          <span class="operator">=</span> <span class="number">122</span>;</span><br><span class="line"> <span class="number">23</span>        ...</span><br><span class="line"> <span class="number">24</span></span><br><span class="line"> <span class="number">25</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">EXECUTE_TRANSACTION</span> <span class="operator">=</span> <span class="number">159</span>;</span><br><span class="line"> <span class="number">26</span></span><br><span class="line"> <span class="number">27</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">RELAUNCH_ACTIVITY</span> <span class="operator">=</span> <span class="number">160</span>;</span><br><span class="line"> <span class="number">28</span>        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">PURGE_RESOURCES</span> <span class="operator">=</span> <span class="number">161</span>;</span><br><span class="line"> <span class="number">29</span></span><br><span class="line"> <span class="number">30</span>        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleMessage</span><span class="params">(Message msg)</span> &#123;</span><br><span class="line"> <span class="number">31</span>            <span class="keyword">if</span> (DEBUG_MESSAGES) Slog.v(TAG, <span class="string">&quot;&gt;&gt;&gt; handling: &quot;</span> + codeToString(msg.what));</span><br><span class="line"> <span class="number">32</span>            <span class="keyword">switch</span> (msg.what) &#123;</span><br><span class="line"> <span class="number">33</span>                <span class="keyword">case</span> BIND_APPLICATION:</span><br><span class="line"> <span class="number">34</span>                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, <span class="string">&quot;bindApplication&quot;</span>);</span><br><span class="line"> <span class="number">35</span>                    <span class="type">AppBindData</span> <span class="variable">data</span> <span class="operator">=</span> (AppBindData)msg.obj;</span><br><span class="line"> <span class="number">36</span>                    handleBindApplication(data);</span><br><span class="line"> <span class="number">37</span>                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);</span><br><span class="line"> <span class="number">38</span>                    <span class="keyword">break</span>;</span><br><span class="line"> <span class="number">39</span>                <span class="keyword">case</span> EXIT_APPLICATION:</span><br><span class="line"> <span class="number">40</span>                    <span class="keyword">if</span> (mInitialApplication != <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="number">41</span>                        mInitialApplication.onTerminate();</span><br><span class="line"> <span class="number">42</span>                    &#125;</span><br><span class="line"> <span class="number">43</span>                    Looper.myLooper().quit();</span><br><span class="line"> <span class="number">44</span>                    <span class="keyword">break</span>;</span><br><span class="line"> <span class="number">45</span>                <span class="keyword">case</span> RECEIVER:</span><br><span class="line"> <span class="number">46</span>                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, <span class="string">&quot;broadcastReceiveComp&quot;</span>);</span><br><span class="line"> <span class="number">47</span>                    handleReceiver((ReceiverData)msg.obj);</span><br><span class="line"> <span class="number">48</span>                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);</span><br><span class="line"> <span class="number">49</span>                    <span class="keyword">break</span>;</span><br><span class="line"> <span class="number">50</span>                <span class="keyword">case</span> CREATE_SERVICE:</span><br><span class="line"> <span class="number">51</span>                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, (<span class="string">&quot;serviceCreate: &quot;</span> + String.valueOf(msg.obj)));</span><br><span class="line"> <span class="number">52</span>                    handleCreateService((CreateServiceData)msg.obj);</span><br><span class="line"> <span class="number">53</span>                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);</span><br><span class="line"> <span class="number">54</span>                    <span class="keyword">break</span>;</span><br><span class="line"> <span class="number">55</span>                <span class="keyword">case</span> BIND_SERVICE:</span><br><span class="line"> <span class="number">56</span>                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, <span class="string">&quot;serviceBind&quot;</span>);</span><br><span class="line"> <span class="number">57</span>                    handleBindService((BindServiceData)msg.obj);</span><br><span class="line"> <span class="number">58</span>                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);</span><br><span class="line"> <span class="number">59</span>                    <span class="keyword">break</span>;</span><br><span class="line"> <span class="number">60</span>                <span class="keyword">case</span> UNBIND_SERVICE:</span><br><span class="line"> <span class="number">61</span>                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, <span class="string">&quot;serviceUnbind&quot;</span>);</span><br><span class="line"> <span class="number">62</span>                    handleUnbindService((BindServiceData)msg.obj);</span><br><span class="line"> <span class="number">63</span>                    schedulePurgeIdler();</span><br><span class="line"> <span class="number">64</span>                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);</span><br><span class="line"> <span class="number">65</span>                    <span class="keyword">break</span>;</span><br><span class="line"> <span class="number">66</span>                <span class="keyword">case</span> SERVICE_ARGS:</span><br><span class="line"> <span class="number">67</span>                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, (<span class="string">&quot;serviceStart: &quot;</span> + String.valueOf(msg.obj)));</span><br><span class="line"> <span class="number">68</span>                    handleServiceArgs((ServiceArgsData)msg.obj);</span><br><span class="line"> <span class="number">69</span>                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);</span><br><span class="line"> <span class="number">70</span>                    <span class="keyword">break</span>;</span><br><span class="line"> <span class="number">71</span>                <span class="keyword">case</span> STOP_SERVICE:</span><br><span class="line"> <span class="number">72</span>                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, <span class="string">&quot;serviceStop&quot;</span>);</span><br><span class="line"> <span class="number">73</span>                    handleStopService((IBinder)msg.obj);</span><br><span class="line"> <span class="number">74</span>                    schedulePurgeIdler();</span><br><span class="line"> <span class="number">75</span>                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);</span><br><span class="line"> <span class="number">76</span>                    <span class="keyword">break</span>;</span><br><span class="line"> <span class="number">77</span>                <span class="keyword">case</span> CONFIGURATION_CHANGED:</span><br><span class="line"> <span class="number">78</span>                    handleConfigurationChanged((Configuration) msg.obj);</span><br><span class="line"> <span class="number">79</span>                    <span class="keyword">break</span>;</span><br><span class="line"> <span class="number">80</span></span><br><span class="line"> <span class="number">81</span>                ...</span><br><span class="line"> <span class="number">82</span></span><br><span class="line"> <span class="number">83</span>                <span class="keyword">case</span> EXECUTE_TRANSACTION:</span><br><span class="line"> <span class="number">84</span>                    <span class="keyword">final</span> <span class="type">ClientTransaction</span> <span class="variable">transaction</span> <span class="operator">=</span> (ClientTransaction) msg.obj;</span><br><span class="line"> <span class="number">85</span>                    mTransactionExecutor.execute(transaction);</span><br><span class="line"> <span class="number">86</span>                    <span class="keyword">if</span> (isSystem()) &#123;</span><br><span class="line"> <span class="number">87</span>                        <span class="comment">// Client transactions inside system process are recycled on the client side</span></span><br><span class="line"> <span class="number">88</span>                        <span class="comment">// instead of ClientLifecycleManager to avoid being cleared before this</span></span><br><span class="line"> <span class="number">89</span>                        <span class="comment">// message is handled.</span></span><br><span class="line"> <span class="number">90</span>                        transaction.recycle();</span><br><span class="line"> <span class="number">91</span>                    &#125;</span><br><span class="line"> <span class="number">92</span>                    <span class="comment">// TODO(lifecycler): Recycle locally scheduled transactions.</span></span><br><span class="line"> <span class="number">93</span>                    <span class="keyword">break</span>;</span><br><span class="line"> <span class="number">94</span>                <span class="keyword">case</span> RELAUNCH_ACTIVITY:</span><br><span class="line"> <span class="number">95</span>                    handleRelaunchActivityLocally((IBinder) msg.obj);</span><br><span class="line"> <span class="number">96</span>                    <span class="keyword">break</span>;</span><br><span class="line"> <span class="number">97</span>                <span class="keyword">case</span> PURGE_RESOURCES:</span><br><span class="line"> <span class="number">98</span>                    schedulePurgeIdler();</span><br><span class="line"> <span class="number">99</span>                    <span class="keyword">break</span>;</span><br><span class="line"><span class="number">100</span>            &#125;</span><br><span class="line"><span class="number">101</span>            <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> msg.obj;</span><br><span class="line"><span class="number">102</span>            <span class="keyword">if</span> (obj <span class="keyword">instanceof</span> SomeArgs) &#123;</span><br><span class="line"><span class="number">103</span>                ((SomeArgs) obj).recycle();</span><br><span class="line"><span class="number">104</span>            &#125;</span><br><span class="line"><span class="number">105</span>            <span class="keyword">if</span> (DEBUG_MESSAGES) Slog.v(TAG, <span class="string">&quot;&lt;&lt;&lt; done: &quot;</span> + codeToString(msg.what));</span><br><span class="line"><span class="number">106</span>        &#125;</span><br><span class="line"><span class="number">107</span>    &#125;</span><br></pre></td></tr></table></figure><p>mH是在创建ActivityThread实例时赋值的，是自定义<a href="https://cloud.tencent.com/developer/tools/blog-entry?target=http://mp.weixin.qq.com/s?__biz=MzU2NjgwNjc0NQ==&mid=2247483748&idx=1&sn=2fbcaba7766da1605f1bf924ddb75ac0&chksm=fca790d2cbd019c4ec2b6583960d3a5a83a16a9b98a48a99503c4a267ce7ff89747b08da12cc&scene=21%23wechat_redirect&source=article&objectId=1666801">Handler</a>子类H的实例，也就是在ActivityThread的main方法中，并且初始化是已经主线程已经有了mainLooper，所以，<strong>使用这个mH来sendMessage就把消息发送到了主线程</strong>。</p><p>那么是从哪个线程发送的呢？那就要看看ApplicationThread的scheduleTransaction方法是执行在哪个线程了。根据<a href="https://cloud.tencent.com/developer/tools/blog-entry?target=http://mp.weixin.qq.com/s?__biz=MzU2NjgwNjc0NQ==&mid=2247483688&idx=1&sn=b141672a67b2d522e0597d8fccb1c294&chksm=fca7909ecbd019887a3a3cc8fdc96a3c5eb9d8fe72ca6650d753bb6dde1f40e0877faea7f3d7&scene=21%23wechat_redirect&source=article&objectId=1666801">IPC</a>知识，我们知道，<a href="https://cloud.tencent.com/act/pro/promotion-cvm?from_column=20065&from=20065">服务器</a>的Binder方法运行在Binder的线程池中，也就是说系统进行跨进程调用ApplicationThread的scheduleTransaction就是执行在Binder的线程池中的了。</p><p>到这里，消息就在主线程处理了，那么是怎么处理Activity的启动的呢？接着看。我们找到ActivityThread.H.EXECUTE_TRANSACTION这个消息的处理，就在handleMessage方法的倒数第三个case（就在上面代码）：取出ClientTransaction实例，调用TransactionExecutor的execute方法，那就看看：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(ClientTransaction transaction)</span> &#123;</span><br><span class="line"> <span class="number">2</span>        <span class="keyword">if</span> (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + <span class="string">&quot;Start resolving transaction&quot;</span>);</span><br><span class="line"> <span class="number">3</span></span><br><span class="line"> <span class="number">4</span>        <span class="keyword">final</span> <span class="type">IBinder</span> <span class="variable">token</span> <span class="operator">=</span> transaction.getActivityToken();</span><br><span class="line"> <span class="number">5</span>        ...</span><br><span class="line"> <span class="number">6</span>        executeCallbacks(transaction);</span><br><span class="line"> <span class="number">7</span></span><br><span class="line"> <span class="number">8</span>        executeLifecycleState(transaction);</span><br><span class="line"> <span class="number">9</span>        ...</span><br><span class="line"><span class="number">10</span>    &#125;</span><br></pre></td></tr></table></figure><p>继续跟进executeCallbacks方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">executeCallbacks</span><span class="params">(ClientTransaction transaction)</span> &#123;</span><br><span class="line"> <span class="number">2</span>        <span class="keyword">final</span> List&lt;ClientTransactionItem&gt; callbacks = transaction.getCallbacks();</span><br><span class="line"> <span class="number">3</span>        <span class="keyword">if</span> (callbacks == <span class="literal">null</span> || callbacks.isEmpty()) &#123;</span><br><span class="line"> <span class="number">4</span>            <span class="comment">// No callbacks to execute, return early.</span></span><br><span class="line"> <span class="number">5</span>            <span class="keyword">return</span>;</span><br><span class="line"> <span class="number">6</span>        &#125;</span><br><span class="line"> <span class="number">7</span>        <span class="keyword">if</span> (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + <span class="string">&quot;Resolving callbacks in transaction&quot;</span>);</span><br><span class="line"> <span class="number">8</span></span><br><span class="line"> <span class="number">9</span>        <span class="keyword">final</span> <span class="type">IBinder</span> <span class="variable">token</span> <span class="operator">=</span> transaction.getActivityToken();</span><br><span class="line"><span class="number">10</span>        <span class="type">ActivityClientRecord</span> <span class="variable">r</span> <span class="operator">=</span> mTransactionHandler.getActivityClient(token);</span><br><span class="line"><span class="number">11</span></span><br><span class="line"><span class="number">12</span>        <span class="comment">// In case when post-execution state of the last callback matches the final state requested</span></span><br><span class="line"><span class="number">13</span>        <span class="comment">// for the activity in this transaction, we won&#x27;t do the last transition here and do it when</span></span><br><span class="line"><span class="number">14</span>        <span class="comment">// moving to final state instead (because it may contain additional parameters from server).</span></span><br><span class="line"><span class="number">15</span>        <span class="keyword">final</span> <span class="type">ActivityLifecycleItem</span> <span class="variable">finalStateRequest</span> <span class="operator">=</span> transaction.getLifecycleStateRequest();</span><br><span class="line"><span class="number">16</span>        <span class="keyword">final</span> <span class="type">int</span> <span class="variable">finalState</span> <span class="operator">=</span> finalStateRequest != <span class="literal">null</span> ? finalStateRequest.getTargetState()</span><br><span class="line"><span class="number">17</span>                : UNDEFINED;</span><br><span class="line"><span class="number">18</span>        <span class="comment">// Index of the last callback that requests some post-execution state.</span></span><br><span class="line"><span class="number">19</span>        <span class="keyword">final</span> <span class="type">int</span> <span class="variable">lastCallbackRequestingState</span> <span class="operator">=</span> lastCallbackRequestingState(transaction);</span><br><span class="line"><span class="number">20</span></span><br><span class="line"><span class="number">21</span>        <span class="keyword">final</span> <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> callbacks.size();</span><br><span class="line"><span class="number">22</span>        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; size; ++i) &#123;</span><br><span class="line"><span class="number">23</span>            <span class="keyword">final</span> <span class="type">ClientTransactionItem</span> <span class="variable">item</span> <span class="operator">=</span> callbacks.get(i);</span><br><span class="line"><span class="number">24</span>            ...</span><br><span class="line"><span class="number">25</span>            item.execute(mTransactionHandler, token, mPendingActions);</span><br><span class="line"><span class="number">26</span>            item.postExecute(mTransactionHandler, token, mPendingActions);</span><br><span class="line"><span class="number">27</span>            ...</span><br><span class="line"><span class="number">28</span>        &#125;</span><br><span class="line"><span class="number">29</span>    &#125;</span><br></pre></td></tr></table></figure><p>遍历callbacks，调用ClientTransactionItem的execute方法，而我们这里要关注的是ClientTransactionItem的子类LaunchActivityItem，看下它的execute方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(ClientTransactionHandler client, IBinder token,</span></span><br><span class="line"><span class="params"> <span class="number">2</span>            PendingTransactionActions pendingActions)</span> &#123;</span><br><span class="line"> <span class="number">3</span>        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, <span class="string">&quot;activityStart&quot;</span>);</span><br><span class="line"> <span class="number">4</span>        <span class="type">ActivityClientRecord</span> <span class="variable">r</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ActivityClientRecord</span>(token, mIntent, mIdent, mInfo,</span><br><span class="line"> <span class="number">5</span>                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,</span><br><span class="line"> <span class="number">6</span>                mPendingResults, mPendingNewIntents, mIsForward,</span><br><span class="line"> <span class="number">7</span>                mProfilerInfo, client, mAssistToken);</span><br><span class="line"> <span class="number">8</span>        client.handleLaunchActivity(r, pendingActions, <span class="literal">null</span> <span class="comment">/* customIntent */</span>);</span><br><span class="line"> <span class="number">9</span>        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);</span><br><span class="line"><span class="number">10</span>    &#125;</span><br></pre></td></tr></table></figure><p>里面调用了client.handleLaunchActivity方法，client是ClientTransactionHandler的实例，是在TransactionExecutor构造方法传入的，TransactionExecutor创建是在ActivityThread中：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span><span class="comment">//ActivityThread</span></span><br><span class="line">2private <span class="keyword">final</span> <span class="type">TransactionExecutor</span> <span class="variable">mTransactionExecutor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TransactionExecutor</span>(<span class="built_in">this</span>);</span><br></pre></td></tr></table></figure><p>所以，client.handleLaunchActivity方法就是ActivityThread的handleLaunchActivity方法。</p><p>好了，到这里 <strong>ApplicationThread把启动Activity的操作，通过mH切到了主线程，走到了ActivityThread的handleLaunchActivity方法</strong>。</p><h3 id="Activity启动核心实现——初始化及生命周期"><a href="#Activity启动核心实现——初始化及生命周期" class="headerlink" title="Activity启动核心实现——初始化及生命周期"></a>Activity启动核心实现——初始化及生命周期</h3><p>接下来</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>    <span class="keyword">public</span> Activity <span class="title function_">handleLaunchActivity</span><span class="params">(ActivityClientRecord r,</span></span><br><span class="line"><span class="params"><span class="number">2</span>            PendingTransactionActions pendingActions, Intent customIntent)</span> &#123;</span><br><span class="line"><span class="number">3</span>        ...</span><br><span class="line"><span class="number">4</span>        <span class="keyword">final</span> <span class="type">Activity</span> <span class="variable">a</span> <span class="operator">=</span> performLaunchActivity(r, customIntent);</span><br><span class="line"><span class="number">5</span>        ...</span><br><span class="line"><span class="number">6</span>        <span class="keyword">return</span> a;</span><br><span class="line"><span class="number">7</span>    &#125;</span><br></pre></td></tr></table></figure><p>继续跟<strong>performLaunchActivity方法，这里就是activity 启动的核心实现</strong>了：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line">  <span class="number">1</span>    <span class="comment">/**  activity 启动的核心实现. */</span></span><br><span class="line">  <span class="number">2</span>    <span class="keyword">private</span> Activity <span class="title function_">performLaunchActivity</span><span class="params">(ActivityClientRecord r, Intent customIntent)</span> &#123;</span><br><span class="line">  <span class="number">3</span>        <span class="comment">//1、从ActivityClientRecord获取待启动的Activity的组件信息</span></span><br><span class="line">  <span class="number">4</span>        <span class="type">ActivityInfo</span> <span class="variable">aInfo</span> <span class="operator">=</span> r.activityInfo;</span><br><span class="line">  <span class="number">5</span>        <span class="keyword">if</span> (r.packageInfo == <span class="literal">null</span>) &#123;</span><br><span class="line">  <span class="number">6</span>            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,</span><br><span class="line">  <span class="number">7</span>                    Context.CONTEXT_INCLUDE_CODE);</span><br><span class="line">  <span class="number">8</span>        &#125;</span><br><span class="line">  <span class="number">9</span></span><br><span class="line"> <span class="number">10</span>        <span class="type">ComponentName</span> <span class="variable">component</span> <span class="operator">=</span> r.intent.getComponent();</span><br><span class="line"> <span class="number">11</span>        <span class="keyword">if</span> (component == <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="number">12</span>            component = r.intent.resolveActivity(</span><br><span class="line"> <span class="number">13</span>                mInitialApplication.getPackageManager());</span><br><span class="line"> <span class="number">14</span>            r.intent.setComponent(component);</span><br><span class="line"> <span class="number">15</span>        &#125;</span><br><span class="line"> <span class="number">16</span></span><br><span class="line"> <span class="number">17</span>        <span class="keyword">if</span> (r.activityInfo.targetActivity != <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="number">18</span>            component = <span class="keyword">new</span> <span class="title class_">ComponentName</span>(r.activityInfo.packageName,</span><br><span class="line"> <span class="number">19</span>                    r.activityInfo.targetActivity);</span><br><span class="line"> <span class="number">20</span>        &#125;</span><br><span class="line"> <span class="number">21</span>        <span class="comment">//创建ContextImpl对象</span></span><br><span class="line"> <span class="number">22</span>        <span class="type">ContextImpl</span> <span class="variable">appContext</span> <span class="operator">=</span> createBaseContextForActivity(r);</span><br><span class="line"> <span class="number">23</span>        <span class="type">Activity</span> <span class="variable">activity</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="number">24</span>        <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="number">25</span>            <span class="comment">//2、创建activity实例</span></span><br><span class="line"> <span class="number">26</span>            java.lang.<span class="type">ClassLoader</span> <span class="variable">cl</span> <span class="operator">=</span> appContext.getClassLoader();</span><br><span class="line"> <span class="number">27</span>            activity = mInstrumentation.newActivity(</span><br><span class="line"> <span class="number">28</span>                    cl, component.getClassName(), r.intent);</span><br><span class="line"> <span class="number">29</span>            StrictMode.incrementExpectedActivityCount(activity.getClass());</span><br><span class="line"> <span class="number">30</span>            r.intent.setExtrasClassLoader(cl);</span><br><span class="line"> <span class="number">31</span>            r.intent.prepareToEnterProcess();</span><br><span class="line"> <span class="number">32</span>            <span class="keyword">if</span> (r.state != <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="number">33</span>                r.state.setClassLoader(cl);</span><br><span class="line"> <span class="number">34</span>            &#125;</span><br><span class="line"> <span class="number">35</span>        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line"> <span class="number">36</span>            ..</span><br><span class="line"> <span class="number">37</span>        &#125;</span><br><span class="line"> <span class="number">38</span>        <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="number">39</span>            <span class="comment">//3、创建Application对象（如果没有的话）</span></span><br><span class="line"> <span class="number">40</span>            <span class="type">Application</span> <span class="variable">app</span> <span class="operator">=</span> r.packageInfo.makeApplication(<span class="literal">false</span>, mInstrumentation);</span><br><span class="line"> <span class="number">41</span>            ...</span><br><span class="line"> <span class="number">42</span>            <span class="keyword">if</span> (activity != <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="number">43</span>                <span class="type">CharSequence</span> <span class="variable">title</span> <span class="operator">=</span> r.activityInfo.loadLabel(appContext.getPackageManager());</span><br><span class="line"> <span class="number">44</span>                <span class="type">Configuration</span> <span class="variable">config</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Configuration</span>(mCompatConfiguration);</span><br><span class="line"> <span class="number">45</span>                <span class="keyword">if</span> (r.overrideConfig != <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="number">46</span>                    config.updateFrom(r.overrideConfig);</span><br><span class="line"> <span class="number">47</span>                &#125;</span><br><span class="line"> <span class="number">48</span></span><br><span class="line"> <span class="number">49</span>                <span class="type">Window</span> <span class="variable">window</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="number">50</span>                <span class="keyword">if</span> (r.mPendingRemoveWindow != <span class="literal">null</span> &amp;&amp; r.mPreserveWindow) &#123;</span><br><span class="line"> <span class="number">51</span>                    window = r.mPendingRemoveWindow;</span><br><span class="line"> <span class="number">52</span>                    r.mPendingRemoveWindow = <span class="literal">null</span>;</span><br><span class="line"> <span class="number">53</span>                    r.mPendingRemoveWindowManager = <span class="literal">null</span>;</span><br><span class="line"> <span class="number">54</span>                &#125;</span><br><span class="line"> <span class="number">55</span>                appContext.setOuterContext(activity);</span><br><span class="line"> <span class="number">56</span></span><br><span class="line"> <span class="number">57</span>                <span class="comment">//4、attach方法为activity关联上下文环境</span></span><br><span class="line"> <span class="number">58</span>                activity.attach(appContext, <span class="built_in">this</span>, getInstrumentation(), r.token,</span><br><span class="line"> <span class="number">59</span>                        r.ident, app, r.intent, r.activityInfo, title, r.parent,</span><br><span class="line"> <span class="number">60</span>                        r.embeddedID, r.lastNonConfigurationInstances, config,</span><br><span class="line"> <span class="number">61</span>                        r.referrer, r.voiceInteractor, window, r.configCallback,</span><br><span class="line"> <span class="number">62</span>                        r.assistToken);</span><br><span class="line"> <span class="number">63</span></span><br><span class="line"> <span class="number">64</span>                <span class="keyword">if</span> (customIntent != <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="number">65</span>                    activity.mIntent = customIntent;</span><br><span class="line"> <span class="number">66</span>                &#125;</span><br><span class="line"> <span class="number">67</span>                r.lastNonConfigurationInstances = <span class="literal">null</span>;</span><br><span class="line"> <span class="number">68</span>                checkAndBlockForNetworkAccess();</span><br><span class="line"> <span class="number">69</span>                activity.mStartedActivity = <span class="literal">false</span>;</span><br><span class="line"> <span class="number">70</span>                <span class="type">int</span> <span class="variable">theme</span> <span class="operator">=</span> r.activityInfo.getThemeResource();</span><br><span class="line"> <span class="number">71</span>                <span class="keyword">if</span> (theme != <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="number">72</span>                    activity.setTheme(theme);</span><br><span class="line"> <span class="number">73</span>                &#125;</span><br><span class="line"> <span class="number">74</span></span><br><span class="line"> <span class="number">75</span>                activity.mCalled = <span class="literal">false</span>;</span><br><span class="line"> <span class="number">76</span></span><br><span class="line"> <span class="number">77</span>                <span class="comment">//5、调用生命周期onCreate</span></span><br><span class="line"> <span class="number">78</span>                <span class="keyword">if</span> (r.isPersistable()) &#123;</span><br><span class="line"> <span class="number">79</span>                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);</span><br><span class="line"> <span class="number">80</span>                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="number">81</span>                    mInstrumentation.callActivityOnCreate(activity, r.state);</span><br><span class="line"> <span class="number">82</span>                &#125;</span><br><span class="line"> <span class="number">83</span>                <span class="keyword">if</span> (!activity.mCalled) &#123;</span><br><span class="line"> <span class="number">84</span>                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">SuperNotCalledException</span>(</span><br><span class="line"> <span class="number">85</span>                        <span class="string">&quot;Activity &quot;</span> + r.intent.getComponent().toShortString() +</span><br><span class="line"> <span class="number">86</span>                        <span class="string">&quot; did not call through to super.onCreate()&quot;</span>);</span><br><span class="line"> <span class="number">87</span>                &#125;</span><br><span class="line"> <span class="number">88</span>                r.activity = activity;</span><br><span class="line"> <span class="number">89</span>            &#125;</span><br><span class="line"> <span class="number">90</span>            r.setState(ON_CREATE);</span><br><span class="line"> <span class="number">91</span></span><br><span class="line"> <span class="number">92</span>            <span class="keyword">synchronized</span> (mResourcesManager) &#123;</span><br><span class="line"> <span class="number">93</span>                mActivities.put(r.token, r);</span><br><span class="line"> <span class="number">94</span>            &#125;</span><br><span class="line"> <span class="number">95</span></span><br><span class="line"> <span class="number">96</span>        &#125; </span><br><span class="line"> <span class="number">97</span>        ...</span><br><span class="line"> <span class="number">98</span></span><br><span class="line"> <span class="number">99</span>        <span class="keyword">return</span> activity;</span><br><span class="line"><span class="number">100</span>    &#125;</span><br></pre></td></tr></table></figure><p>performLaunchActivity主要完成以下事情：</p><ol><li>从ActivityClientRecord获取待启动的Activity的组件信息</li><li>通过mInstrumentation.newActivity方法使用类加载器创建activity实例</li><li>通过LoadedApk的makeApplication方法创建Application对象，内部也是通过mInstrumentation使用类加载器，创建后就调用了instrumentation.callApplicationOnCreate方法，也就是Application的onCreate方法。</li><li>创建ContextImpl对象并通过activity.attach方法对重要数据初始化，关联了Context的具体实现ContextImpl，attach方法内部还完成了window创建，这样Window接收到外部事件后就能传递给Activity了。</li><li>调用Activity的onCreate方法，是通过 mInstrumentation.callActivityOnCreate方法完成。</li></ol><p><strong>到这里Activity的onCreate方法执行完，那么onStart、onResume呢？</strong></p><p>上面看到LaunchActivityItem，是用来启动Activity的，也就是走到Activity的onCreate，那么是不是有 “XXXActivityItem”呢? 有的：</p><ul><li>LaunchActivityItem 远程App端的onCreate生命周期事务</li><li>ResumeActivityItem 远程App端的onResume生命周期事务</li><li>PauseActivityItem 远程App端的onPause生命周期事务</li><li>StopActivityItem 远程App端的onStop生命周期事务</li><li>DestroyActivityItem 远程App端onDestroy生命周期事务</li></ul><p>另外梳理过程中涉及的几个类：</p><ul><li>ClientTransaction 客户端事务控制者</li><li>ClientLifecycleManager 客户端的生命周期事务控制者</li><li>TransactionExecutor 远程通信事务执行者</li></ul><p>那么我们再来看看ResumeActivityItem吧。</p><p>我们再来重新看看在ActivityStackSupervisor的realStartActivityLocked方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="type">boolean</span> <span class="title function_">realStartActivityLocked</span><span class="params">(ActivityRecord r, WindowProcessController proc,</span></span><br><span class="line"><span class="params"> <span class="number">2</span>            <span class="type">boolean</span> andResume, <span class="type">boolean</span> checkConfig)</span> <span class="keyword">throws</span> RemoteException &#123;</span><br><span class="line"> <span class="number">3</span>            ...</span><br><span class="line"> <span class="number">4</span>                <span class="comment">// Create activity launch transaction.</span></span><br><span class="line"> <span class="number">5</span>                <span class="keyword">final</span> <span class="type">ClientTransaction</span> <span class="variable">clientTransaction</span> <span class="operator">=</span> ClientTransaction.obtain(</span><br><span class="line"> <span class="number">6</span>                        proc.getThread(), r.appToken);</span><br><span class="line"> <span class="number">7</span></span><br><span class="line"> <span class="number">8</span>                <span class="keyword">final</span> <span class="type">DisplayContent</span> <span class="variable">dc</span> <span class="operator">=</span> r.getDisplay().mDisplayContent;</span><br><span class="line"> <span class="number">9</span>                clientTransaction.addCallback(LaunchActivityItem.obtain(<span class="keyword">new</span> <span class="title class_">Intent</span>(r.intent),</span><br><span class="line"><span class="number">10</span>                        System.identityHashCode(r), r.info,</span><br><span class="line"><span class="number">11</span>                        mergedConfiguration.getGlobalConfiguration(),</span><br><span class="line"><span class="number">12</span>                        mergedConfiguration.getOverrideConfiguration(), r.compat,</span><br><span class="line"><span class="number">13</span>                        r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),</span><br><span class="line"><span class="number">14</span>                        r.icicle, r.persistentState, results, newIntents,</span><br><span class="line"><span class="number">15</span>                        dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),</span><br><span class="line"><span class="number">16</span>                                r.assistToken));</span><br><span class="line"><span class="number">17</span></span><br><span class="line"><span class="number">18</span>                <span class="comment">// Set desired final state.</span></span><br><span class="line"><span class="number">19</span>                <span class="keyword">final</span> ActivityLifecycleItem lifecycleItem;</span><br><span class="line"><span class="number">20</span>                <span class="comment">//这里ResumeActivityItem</span></span><br><span class="line"><span class="number">21</span>                <span class="keyword">if</span> (andResume) &#123;</span><br><span class="line"><span class="number">22</span>                    lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());</span><br><span class="line"><span class="number">23</span>                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"><span class="number">24</span>                    lifecycleItem = PauseActivityItem.obtain();</span><br><span class="line"><span class="number">25</span>                &#125;</span><br><span class="line"><span class="number">26</span>                clientTransaction.setLifecycleStateRequest(lifecycleItem);</span><br><span class="line"><span class="number">27</span></span><br><span class="line"><span class="number">28</span>                <span class="comment">// Schedule transaction.</span></span><br><span class="line"><span class="number">29</span>                mService.getLifecycleManager().scheduleTransaction(clientTransaction);</span><br><span class="line"><span class="number">30</span></span><br><span class="line"><span class="number">31</span>                ...</span><br><span class="line"><span class="number">32</span></span><br><span class="line"><span class="number">33</span>        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"><span class="number">34</span>    &#125;</span><br></pre></td></tr></table></figure><p>前面只说了通过clientTransaction.addCallback添加LaunchActivityItem实例，在注意下面接着调用了clientTransaction.setLifecycleStateRequest(lifecycleItem)方法，lifecycleItem是ResumeActivityItem或PauseActivityItem实例，这里我们关注ResumeActivityItem，先看下setLifecycleStateRequest方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>    <span class="comment">/**</span></span><br><span class="line"><span class="comment">2     * Final lifecycle state in which the client activity should be after the transaction is</span></span><br><span class="line"><span class="comment">3     * executed.</span></span><br><span class="line"><span class="comment">4     */</span></span><br><span class="line"><span class="number">5</span>    <span class="keyword">private</span> ActivityLifecycleItem mLifecycleStateRequest;</span><br><span class="line"><span class="number">6</span></span><br><span class="line"><span class="number">7</span>    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setLifecycleStateRequest</span><span class="params">(ActivityLifecycleItem stateRequest)</span> &#123;</span><br><span class="line"><span class="number">8</span>        mLifecycleStateRequest = stateRequest;</span><br><span class="line"><span class="number">9</span>    &#125;</span><br></pre></td></tr></table></figure><p>mLifecycleStateRequest表示执行transaction后的最终的生命周期状态。</p><p>继续看处理ActivityThread.H.EXECUTE_TRANSACTION这个消息的处理，即TransactionExecutor的execute方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(ClientTransaction transaction)</span> &#123;</span><br><span class="line"> <span class="number">2</span>        <span class="keyword">if</span> (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + <span class="string">&quot;Start resolving transaction&quot;</span>);</span><br><span class="line"> <span class="number">3</span></span><br><span class="line"> <span class="number">4</span>        <span class="keyword">final</span> <span class="type">IBinder</span> <span class="variable">token</span> <span class="operator">=</span> transaction.getActivityToken();</span><br><span class="line"> <span class="number">5</span>        ...</span><br><span class="line"> <span class="number">6</span>        executeCallbacks(transaction);</span><br><span class="line"> <span class="number">7</span></span><br><span class="line"> <span class="number">8</span>        executeLifecycleState(transaction);</span><br><span class="line"> <span class="number">9</span>        ...</span><br><span class="line"><span class="number">10</span>    &#125;</span><br></pre></td></tr></table></figure><p>前面我们关注的是executeCallbacks方法，现在看看executeLifecycleState方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="comment">/** Transition to the final state if requested by the transaction. */</span></span><br><span class="line"> <span class="number">2</span>    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">executeLifecycleState</span><span class="params">(ClientTransaction transaction)</span> &#123;</span><br><span class="line"> <span class="number">3</span>        <span class="keyword">final</span> <span class="type">ActivityLifecycleItem</span> <span class="variable">lifecycleItem</span> <span class="operator">=</span> transaction.getLifecycleStateRequest();</span><br><span class="line"> <span class="number">4</span>        <span class="keyword">if</span> (lifecycleItem == <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="number">5</span>            <span class="comment">// No lifecycle request, return early.</span></span><br><span class="line"> <span class="number">6</span>            <span class="keyword">return</span>;</span><br><span class="line"> <span class="number">7</span>        &#125;</span><br><span class="line"> <span class="number">8</span></span><br><span class="line"> <span class="number">9</span>        <span class="keyword">final</span> <span class="type">IBinder</span> <span class="variable">token</span> <span class="operator">=</span> transaction.getActivityToken();</span><br><span class="line"><span class="number">10</span>        <span class="keyword">final</span> <span class="type">ActivityClientRecord</span> <span class="variable">r</span> <span class="operator">=</span> mTransactionHandler.getActivityClient(token);</span><br><span class="line"><span class="number">11</span>        ...</span><br><span class="line"><span class="number">12</span></span><br><span class="line"><span class="number">13</span>        <span class="comment">// Execute the final transition with proper parameters.</span></span><br><span class="line"><span class="number">14</span>        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);</span><br><span class="line"><span class="number">15</span>        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);</span><br><span class="line"><span class="number">16</span>    &#125;</span><br></pre></td></tr></table></figure><p>这里取出了ActivityLifecycleItem并且调用了它的execute方法，实际就是ResumeActivityItem的方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>    <span class="meta">@Override</span></span><br><span class="line"><span class="number">2</span>    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(ClientTransactionHandler client, IBinder token,</span></span><br><span class="line"><span class="params"><span class="number">3</span>            PendingTransactionActions pendingActions)</span> &#123;</span><br><span class="line"><span class="number">4</span>        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, <span class="string">&quot;activityResume&quot;</span>);</span><br><span class="line"><span class="number">5</span>        client.handleResumeActivity(token, <span class="literal">true</span> <span class="comment">/* finalStateRequest */</span>, mIsForward,</span><br><span class="line"><span class="number">6</span>                <span class="string">&quot;RESUME_ACTIVITY&quot;</span>);</span><br><span class="line"><span class="number">7</span>        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);</span><br><span class="line"><span class="number">8</span>    &#125;</span><br></pre></td></tr></table></figure><p>经过上面分析实际是走到ActivityThread的handleResumeActivity方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">1</span>    <span class="meta">@Override</span></span><br><span class="line"> <span class="number">2</span>    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleResumeActivity</span><span class="params">(IBinder token, <span class="type">boolean</span> finalStateRequest, <span class="type">boolean</span> isForward,</span></span><br><span class="line"><span class="params"> <span class="number">3</span>            String reason)</span> &#123;</span><br><span class="line"> <span class="number">4</span>        ...</span><br><span class="line"> <span class="number">5</span>        <span class="comment">// performResumeActivity内部会走onStart、onResume</span></span><br><span class="line"> <span class="number">6</span>        <span class="keyword">final</span> <span class="type">ActivityClientRecord</span> <span class="variable">r</span> <span class="operator">=</span> performResumeActivity(token, finalStateRequest, reason);</span><br><span class="line"> <span class="number">7</span>        <span class="keyword">if</span> (r == <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="number">8</span>            <span class="comment">// We didn&#x27;t actually resume the activity, so skipping any follow-up actions.</span></span><br><span class="line"> <span class="number">9</span>            <span class="keyword">return</span>;</span><br><span class="line"><span class="number">10</span>        &#125;</span><br><span class="line"><span class="number">11</span>        ...</span><br><span class="line"><span class="number">12</span></span><br><span class="line"><span class="number">13</span>        <span class="keyword">if</span> (r.window == <span class="literal">null</span> &amp;&amp; !a.mFinished &amp;&amp; willBeVisible) &#123;</span><br><span class="line"><span class="number">14</span>            r.window = r.activity.getWindow();</span><br><span class="line"><span class="number">15</span>            <span class="type">View</span> <span class="variable">decor</span> <span class="operator">=</span> r.window.getDecorView();</span><br><span class="line"><span class="number">16</span>            decor.setVisibility(View.INVISIBLE);</span><br><span class="line"><span class="number">17</span>            <span class="type">ViewManager</span> <span class="variable">wm</span> <span class="operator">=</span> a.getWindowManager();</span><br><span class="line"><span class="number">18</span>            WindowManager.<span class="type">LayoutParams</span> <span class="variable">l</span> <span class="operator">=</span> r.window.getAttributes();</span><br><span class="line"><span class="number">19</span>            a.mDecor = decor;</span><br><span class="line"><span class="number">20</span>            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;</span><br><span class="line"><span class="number">21</span>            l.softInputMode |= forwardBit;</span><br><span class="line"><span class="number">22</span>            <span class="keyword">if</span> (r.mPreserveWindow) &#123;</span><br><span class="line"><span class="number">23</span>                a.mWindowAdded = <span class="literal">true</span>;</span><br><span class="line"><span class="number">24</span>                r.mPreserveWindow = <span class="literal">false</span>;</span><br><span class="line"><span class="number">25</span></span><br><span class="line"><span class="number">26</span>                <span class="type">ViewRootImpl</span> <span class="variable">impl</span> <span class="operator">=</span> decor.getViewRootImpl();</span><br><span class="line"><span class="number">27</span>                <span class="keyword">if</span> (impl != <span class="literal">null</span>) &#123;</span><br><span class="line"><span class="number">28</span>                    impl.notifyChildRebuilt();</span><br><span class="line"><span class="number">29</span>                &#125;</span><br><span class="line"><span class="number">30</span>            &#125;</span><br><span class="line"><span class="number">31</span>            ...</span><br><span class="line"><span class="number">32</span></span><br><span class="line"><span class="number">33</span>        <span class="keyword">if</span> (!r.activity.mFinished &amp;&amp; willBeVisible &amp;&amp; r.activity.mDecor != <span class="literal">null</span> &amp;&amp; !r.hideForNow) &#123;</span><br><span class="line"><span class="number">34</span>            <span class="keyword">if</span> (r.newConfig != <span class="literal">null</span>) &#123;</span><br><span class="line"><span class="number">35</span>                performConfigurationChangedForActivity(r, r.newConfig);</span><br><span class="line"><span class="number">36</span>                <span class="keyword">if</span> (DEBUG_CONFIGURATION) &#123;</span><br><span class="line"><span class="number">37</span>                    Slog.v(TAG, <span class="string">&quot;Resuming activity &quot;</span> + r.activityInfo.name + <span class="string">&quot; with newConfig &quot;</span></span><br><span class="line"><span class="number">38</span>                            + r.activity.mCurrentConfig);</span><br><span class="line"><span class="number">39</span>                &#125;</span><br><span class="line"><span class="number">40</span>                r.newConfig = <span class="literal">null</span>;</span><br><span class="line"><span class="number">41</span>            &#125;</span><br><span class="line"><span class="number">42</span>            <span class="keyword">if</span> (localLOGV) Slog.v(TAG, <span class="string">&quot;Resuming &quot;</span> + r + <span class="string">&quot; with isForward=&quot;</span> + isForward);</span><br><span class="line"><span class="number">43</span>            WindowManager.<span class="type">LayoutParams</span> <span class="variable">l</span> <span class="operator">=</span> r.window.getAttributes();</span><br><span class="line"><span class="number">44</span>            <span class="keyword">if</span> ((l.softInputMode</span><br><span class="line"><span class="number">45</span>                    &amp; WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)</span><br><span class="line"><span class="number">46</span>                    != forwardBit) &#123;</span><br><span class="line"><span class="number">47</span>                l.softInputMode = (l.softInputMode</span><br><span class="line"><span class="number">48</span>                        &amp; (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))</span><br><span class="line"><span class="number">49</span>                        | forwardBit;</span><br><span class="line"><span class="number">50</span>                <span class="keyword">if</span> (r.activity.mVisibleFromClient) &#123;</span><br><span class="line"><span class="number">51</span>                    <span class="type">ViewManager</span> <span class="variable">wm</span> <span class="operator">=</span> a.getWindowManager();</span><br><span class="line"><span class="number">52</span>                    <span class="type">View</span> <span class="variable">decor</span> <span class="operator">=</span> r.window.getDecorView();</span><br><span class="line"><span class="number">53</span>                    wm.updateViewLayout(decor, l);</span><br><span class="line"><span class="number">54</span>                &#125;</span><br><span class="line"><span class="number">55</span>            &#125;</span><br><span class="line"><span class="number">56</span></span><br><span class="line"><span class="number">57</span>            r.activity.mVisibleFromServer = <span class="literal">true</span>;</span><br><span class="line"><span class="number">58</span>            mNumVisibleActivities++;</span><br><span class="line"><span class="number">59</span>            <span class="keyword">if</span> (r.activity.mVisibleFromClient) &#123;</span><br><span class="line"><span class="number">60</span>                <span class="comment">//添加window、设置可见</span></span><br><span class="line"><span class="number">61</span>                r.activity.makeVisible();</span><br><span class="line"><span class="number">62</span>            &#125;</span><br><span class="line"><span class="number">63</span>        &#125;</span><br><span class="line"><span class="number">64</span></span><br><span class="line"><span class="number">65</span>        r.nextIdle = mNewActivities;</span><br><span class="line"><span class="number">66</span>        mNewActivities = r;</span><br><span class="line"><span class="number">67</span>        <span class="keyword">if</span> (localLOGV) Slog.v(TAG, <span class="string">&quot;Scheduling idle handler for &quot;</span> + r);</span><br><span class="line"><span class="number">68</span>        Looper.myQueue().addIdleHandler(<span class="keyword">new</span> <span class="title class_">Idler</span>());</span><br><span class="line"><span class="number">69</span>    &#125;</span><br></pre></td></tr></table></figure><p>handleResumeActivity做了以下事情：</p><ol><li>通过performResumeActivity方法，内部调用生命周期onStart、onResume（可以自行查看，这里不再扩展）</li><li>通过activity.makeVisible方法，添加window设置可见。（所以视图的真正可见是在onResume方法之后）</li></ol><p>好了，到这里就是真正创建完成并且可见了。</p><p><img src="/../imgs/activity_start_process.png" alt="alt text" title="activity加载过程"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Activity是一种展示型组件，用于展示给用户一个可以交互的页面。Activity是Android中最重要的组件，对用户来说，所有的Activity就是一个App的全部，而其他组件用户是不能直接感知的。在开发层面，要启动一个Activity可以使用Intent，分显式和隐</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="Activity" scheme="https://adewu.github.io/tags/Activity/"/>
    
  </entry>
  
  <entry>
    <title>RecyclerView缓存机制</title>
    <link href="https://adewu.github.io/2016/06/24/recyclerView/"/>
    <id>https://adewu.github.io/2016/06/24/recyclerView/</id>
    <published>2016-06-24T02:52:00.000Z</published>
    <updated>2024-02-11T12:50:16.844Z</updated>
    
    <content type="html"><![CDATA[<p><strong>A flexible view for providing a limited window into a large data set.</strong></p><h3 id="缓存层级"><a href="#缓存层级" class="headerlink" title="缓存层级"></a>缓存层级</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Recycler</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> ArrayList&lt;ViewHolder&gt; mAttachedScrap = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        ArrayList&lt;ViewHolder&gt; mChangedScrap = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">final</span> ArrayList&lt;ViewHolder&gt; mCachedViews = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;ViewHolder&gt;();</span><br><span class="line">        RecycledViewPool mRecyclerPool;</span><br><span class="line">        <span class="keyword">private</span> ViewCacheExtension mViewCacheExtension;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>从上面的代码片段我们可以看到这个类声明了五个成员变量：</p><ol><li>mAttachedScrap:我们可以看到这个变量是个存放ViewHolder对象的ArrayList,这一级缓存是没有容量限制的，只要符合条件的我来者不拒，全收了。前面讲两个专业术语的时候提到了Scrap,这个就属于Scrap中的一种，这里的数据是不做修改的，不会重新走Adapter的绑定方法。</li><li>mChangedScrap:这个变量和上边的mAttachedScrap是一样的，唯一不同的从名字也可以看出来，它存放的是发生了变化的ViewHolder,如果使用到了这里的缓存的ViewHolder是要重新走Adapter的绑定方法的。</li><li>mCachedViews:这个变量同样是一个存放ViewHolder对象的ArrayList,但是这个不同于上面的两个里面存放的是dettach掉的视图，它里面存放的是已经remove掉的视图，已经和RV分离的关系的视图，但是它里面的ViewHolder依然保存着之前的信息，比如position、和绑定的数据等等。这一级缓存是有容量限制的，默认是2（不同版本API可能会有差异，本文基于API26.1.0）。</li><li>mRecyclerPool:这个变量呢本身是一个类，跟上面三个都不一样。这里面保存的ViewHolder不仅仅是removed掉的视图，而且是恢复了出厂设置的视图，任何绑定过的痕迹都没有了，想用这里缓存的ViewHolder那是铁定要重新走Adapter的绑定方法了。而且我们知道RV支持多布局，所以这里的缓存是按照itemType来分开存储的，我们来大致的看一下它的结构：</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">RecycledViewPool</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">DEFAULT_MAX_SCRAP</span> <span class="operator">=</span> <span class="number">5</span>;</span><br><span class="line">        <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">ScrapData</span> &#123;</span><br><span class="line">            ArrayList&lt;ViewHolder&gt; mScrapHeap = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">            <span class="type">int</span> <span class="variable">mMaxScrap</span> <span class="operator">=</span> DEFAULT_MAX_SCRAP;</span><br><span class="line">        &#125;</span><br><span class="line">        SparseArray&lt;ScrapData&gt; mScrap = <span class="keyword">new</span> <span class="title class_">SparseArray</span>&lt;&gt;();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><ul><li>首先我们看到一个常量‘DEFAULT_MAX_SCRAP’，这个就是缓存池定义的一个默认的缓存数，当然这个缓存数我们是可以自己设置的。而且这个缓存数量不是指整个缓存池只能缓存这么多，而是每个不同itemType的ViewHolder的缓存数量。</li><li>接着往下看，我们看到一个静态内部类ScrapData,这里我们只看跟缓存相关的两个变量，先说mMaxScrap，前面的常量赋值给了它，这也就印证了我们前面说的这个缓存数量是对应每一种类型的ViewHolder的。再来看这个mScrapHeap变量，熟悉的一幕又来了，同样是一个缓存ViewHolder对象的ArrayList，它的容量默认是5.</li><li>最后我们看到mScrap这个变量，它是一个存储我们上面提到的ScrapData类的对象的SparseArray，这样我们这个RecyclerPool就把不同itemType的ViewHolder按类型分类缓存了起来。</li></ul><p>mViewCacheExtension：ViewCacheExtension是需要开发者自己实现的缓存，基本上页面上的所有数据都可以通过它进行实现。</p><h3 id="绘制流程"><a href="#绘制流程" class="headerlink" title="绘制流程"></a>绘制流程</h3><p>一个VIew的显示要经过onMeasure、onLayout、onDraw三个方法，那么我们就先从第一个方法onMeasure入手，来看看里面做了什么。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">onMeasure</span><span class="params">(<span class="type">int</span> widthSpec, <span class="type">int</span> heightSpec)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (mLayout == <span class="literal">null</span>) &#123;</span><br><span class="line">            defaultOnMeasure(widthSpec, heightSpec);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (mLayout.mAutoMeasure) &#123;</span><br><span class="line">            <span class="keyword">if</span> (mState.mLayoutStep == State.STEP_START) &#123;</span><br><span class="line">                dispatchLayoutStep1();</span><br><span class="line">            &#125;</span><br><span class="line">            dispatchLayoutStep2();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>dispatchLayoutStep1和2方法，1方法中如果mState.mRunPredictiveAnimations为true会调用mLayout.onLayoutChildren(mRecycler, mState)这个方法，但是一般RV的预测动画都为false，所以我们看一下2方法，方法中同样调用了mLayout.onLayoutChildren(mRecycler, mState)方法，来看一下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">dispatchLayoutStep2</span><span class="params">()</span> &#123;</span><br><span class="line">       eatRequestLayout();</span><br><span class="line">       onEnterLayoutOrScroll();</span><br><span class="line"></span><br><span class="line">       <span class="comment">// Step 2: Run layout</span></span><br><span class="line">       mState.mInPreLayout = <span class="literal">false</span>;</span><br><span class="line">       mLayout.onLayoutChildren(mRecycler, mState);</span><br><span class="line"></span><br><span class="line">       mState.mLayoutStep = State.STEP_ANIMATIONS;</span><br><span class="line">       onExitLayoutOrScroll();</span><br><span class="line">       resumeRequestLayout(<span class="literal">false</span>);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>这里onLayoutChildren方法是必走的，而mLayout是RV的成员变量，也就是LayoutManager,接下来我们去LinearLayoutManager里看看onLayoutChildren方法做了什么。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onLayoutChildren</span><span class="params">(RecyclerView.Recycler recycler, RecyclerView.State state)</span> &#123;</span><br><span class="line"></span><br><span class="line">       detachAndScrapAttachedViews(recycler);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (mAnchorInfo.mLayoutFromEnd) &#123;</span><br><span class="line">           <span class="comment">// fill towards start</span></span><br><span class="line">           fill(recycler, mLayoutState, state, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line">           <span class="comment">// fill towards end</span></span><br><span class="line">           fill(recycler, mLayoutState, state, <span class="literal">false</span>);</span><br><span class="line">           endOffset = mLayoutState.mOffset;</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           <span class="comment">// fill towards end</span></span><br><span class="line">           fill(recycler, mLayoutState, state, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line">           <span class="comment">// fill towards start</span></span><br><span class="line">           fill(recycler, mLayoutState, state, <span class="literal">false</span>);</span><br><span class="line">           startOffset = mLayoutState.mOffset;</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>来看下detachAndScrapAttachedViews(recycler)方法中做了什么。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">detachAndScrapAttachedViews</span><span class="params">(Recycler recycler)</span> &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="type">int</span> <span class="variable">childCount</span> <span class="operator">=</span> getChildCount();</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> childCount - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="type">View</span> <span class="variable">v</span> <span class="operator">=</span> getChildAt(i);</span><br><span class="line">                scrapOrRecycleView(recycler, i, v);</span><br><span class="line">            &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>如果有子view调用了scrapOrRecycleView(recycler, i, v)方法，继续追踪。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">scrapOrRecycleView</span><span class="params">(Recycler recycler, <span class="type">int</span> index, View view)</span> &#123;</span><br><span class="line">       <span class="keyword">final</span> <span class="type">ViewHolder</span> <span class="variable">viewHolder</span> <span class="operator">=</span> getChildViewHolderInt(view);</span><br><span class="line">       <span class="keyword">if</span> (viewHolder.isInvalid() &amp;&amp; !viewHolder.isRemoved()</span><br><span class="line">               &amp;&amp; !mRecyclerView.mAdapter.hasStableIds()) &#123;</span><br><span class="line">           removeViewAt(index);</span><br><span class="line">           recycler.recycleViewHolderInternal(viewHolder);</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           detachViewAt(index);</span><br><span class="line">           recycler.scrapView(view);</span><br><span class="line">           mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>正常开始布局的时候会进入else分支，首先是调用detachViewAt(index)来分离视图，然后调用了recycler.scrapView(view)方法。前面我们说过Recycler是RV的内部类，是管理RV缓存的核心类，然后我们继续追踪这个srapView方法，看看里面做了什么。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">scrapView</span><span class="params">(View view)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ViewHolder</span> <span class="variable">holder</span> <span class="operator">=</span> getChildViewHolderInt(view);</span><br><span class="line">        <span class="keyword">if</span> (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)</span><br><span class="line">                || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) &#123;</span><br><span class="line">            <span class="keyword">if</span> (holder.isInvalid() &amp;&amp; !holder.isRemoved() &amp;&amp; !mAdapter.hasStableIds()) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;……&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            holder.setScrapContainer(<span class="built_in">this</span>, <span class="literal">false</span>);</span><br><span class="line">            mAttachedScrap.add(holder);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>这里我们看到了熟悉的身影，「mAttachedScrap」,到此为止我们知道了，onLayoutChildren方法中调用detachAndScrapAttachedViews方法把存在的子view先分离然后缓存到了AttachedScrap中。我们回到onLayoutChildren方法中看看接下来做了什么，我们发现它先判断了方向，因为LinearLayoutManager有横纵两个方向，无论哪个方向最后都是调用fill方法，见名知意，这是个填充布局的方法，fill方法中又调用了layoutChunk这个方法，我们看一眼这个方法。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">layoutChunk</span><span class="params">(RecyclerView.Recycler recycler, RecyclerView.State state,</span></span><br><span class="line"><span class="params">            LayoutState layoutState, LayoutChunkResult result)</span> &#123;</span><br><span class="line">        <span class="type">View</span> <span class="variable">view</span> <span class="operator">=</span> layoutState.next(recycler);</span><br><span class="line">        <span class="keyword">if</span> (view == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (layoutState.mScrapList == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (mShouldReverseLayout == (layoutState.mLayoutDirection</span><br><span class="line">                    == LayoutState.LAYOUT_START)) &#123;</span><br><span class="line">                addView(view);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                addView(view, <span class="number">0</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>该方法中我们看到通过layoutState.next(recycler)方法来拿到视图，如果这个视图为null那么方法终止，否则就会调用addView方法将视图添加或者重新attach回来，这个我们不关心，我们看看是怎么拿到视图的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">View <span class="title function_">next</span><span class="params">(RecyclerView.Recycler recycler)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (mScrapList != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> nextViewFromScrapList();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">View</span> <span class="variable">view</span> <span class="operator">=</span> recycler.getViewForPosition(mCurrentPosition);</span><br><span class="line">        mCurrentPosition += mItemDirection;</span><br><span class="line">        <span class="keyword">return</span> view;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>首先我们看到如果mScrapList不为空会去其中取视图，mScrapList是什么呢？实际上它就是mAttachedScrap,但是它是只读的，而且只有在开启预测动画时才会被赋值，所以我们忽略它即可。重点关注下recycler.getViewForPosition(mCurrentPosition)方法,这个方法经过层层调用，最终是调用的Recycler类中的「tryGetViewHolderForPositionByDeadline(int position,boolean dryRun,long deadlineNs)」方法，接下来看一下这个方法做了哪些事。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line">    ViewHolder <span class="title function_">tryGetViewHolderForPositionByDeadline</span><span class="params">(<span class="type">int</span> position,</span></span><br><span class="line"><span class="params">            <span class="type">boolean</span> dryRun, <span class="type">long</span> deadlineNs)</span> &#123;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">fromScrapOrHiddenOrCache</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="type">ViewHolder</span> <span class="variable">holder</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="comment">// 0) If there is a changed scrap, try to find from there</span></span><br><span class="line">        <span class="keyword">if</span> (mState.isPreLayout()) &#123;</span><br><span class="line">            holder = getChangedScrapViewForPosition(position);</span><br><span class="line">            fromScrapOrHiddenOrCache = holder != <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 1) Find by position from scrap/hidden list/cache</span></span><br><span class="line">        <span class="keyword">if</span> (holder == <span class="literal">null</span>) &#123;</span><br><span class="line">            holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (holder == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 2) Find from scrap/cache via stable ids, if exists</span></span><br><span class="line">            <span class="keyword">if</span> (holder == <span class="literal">null</span> &amp;&amp; mViewCacheExtension != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="type">View</span> <span class="variable">view</span> <span class="operator">=</span> mViewCacheExtension</span><br><span class="line">                        .getViewForPositionAndType(<span class="built_in">this</span>, position, type);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (holder == <span class="literal">null</span>) &#123;</span><br><span class="line">                holder = getRecycledViewPool().getRecycledView(type);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (holder == <span class="literal">null</span>) &#123;</span><br><span class="line">                holder = mAdapter.createViewHolder(RecyclerView.<span class="built_in">this</span>, type);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> holder;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>这段代码着实做了不少事情，获取View和绑定View都是在这个方法中完成的，当然关于绑定和其它的无关代码这里就不贴了。我们一步步的看一下：</p><p>\1. 第一步先从getChangedScrapViewForPosition(position)方法中找需要的视图，但是有个条件mState.isPreLayout()要为true，这个一般在我们调用adapter的notifyItemChanged等方法时为true，其实也很好理解，数据发生了变化，viewholder被detach掉后缓存在mChangedScrap之中，在这里拿到的viewHolder后续需要重新绑定。</p><p>\2. 第二步，如果没有找到视图则从getScrapOrHiddenOrCachedHolderForPosition这个方法中继续找。这个方法的代码就不贴了，简单说下这里的查找顺序：</p><ul><li>首先从mAttachedScrap中查找</li><li>再次从前面略过的ChildHelper类中的mHiddenViews中查找</li><li>最后是从mCachedViews中查找的</li></ul><p>\3. 第三步， mViewCacheExtension中查找，我们说过这个对象默认是null的，是由我们开发者自定义缓存策略的一层，所以如果你没有定义过，这里是找不到View的。</p><p>\4. 第四步，从RecyclerPool中查找，前面我们介绍过RecyclerPool,先通过itemType从SparseArray类型的mscrap中拿到ScrapData，不为空继续拿到scrapHeap这个ArrayList，然后取到视图，这里拿到的视图需要重新绑定。</p><p>\5. 第五步，如果前面几步都没有拿到视图，那么调用了mAdapter.createViewHolder(RecyclerView.this, type)方法，这个方法内部调用了一个抽象方法onCreateViewHolder,是不是很熟悉，没错，就是我们自己写一个Adapter要实现的方法之一。</p><p>到此为止我们获取一个视图的流程就讲完了，获取到视图之后就是怎么摆放视图并添加到RV之中，然后最终展示到我们面前。细心的小伙伴可能发现这个流程貌似有点问题啊？第一次进入onLayoutChildren时还没有任何子view，在fill方法前等于没有缓存子view，所有的子View都是第五步onCreateViewHolder创建而来的。实际上这里的设计是有道理的，除了一些特殊情况onLayoutChildren方法会被多次调用外，一个View从无到有展示在我们面前要至少经过两次onMeasure,一次onLayout,一次onDraw方法（为什么是这样的呢，感兴趣的小伙伴可以去ViewRootImpl中找找答案）。所以这里需要做个缓存，而不至于每次都重新创建新的视图。整个过程大致如图：</p><p><img src="/../imgs/Android_rv_draw_process.webp" alt="alt text" title="绘制流程"></p><p>这里提一下，在RV展示成功后，Scrap这层的缓存就为空了，在从Scrap中取视图的同时就被移出了缓存。在onLayout这里最终会调用到dispatchLayoutStep3方法，没错，除了1和2还有3，在3中，如果Scrap还有缓存，那么缓存会被清空，清空的缓存会被添加到mCachedViews或者RecyclerPool中。</p><h3 id="滑动时的缓存过程"><a href="#滑动时的缓存过程" class="headerlink" title="滑动时的缓存过程"></a>滑动时的缓存过程</h3><p>RV是可以通过滚动来展示大量数据的控件，那么由当前屏幕滚动而出的View去哪了？滚动而入的View哪来的？同样的，我们去源码中找找答案。</p><p>scrollHorizontallyBy,scrollVerticallyBy</p><ul><li><p>一个LayoutManager如果可以滑动，那么上面的两个方法要返回非0值，分别代表可以横向滚动和纵向滚动。最终两个方法都会调用scrollBy方法，然后scrollby方法调用了fill方法，这个fill我们已经见过了，现在再看一下。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">fill</span><span class="params">(RecyclerView.Recycler recycler, LayoutState layoutState,</span></span><br><span class="line"><span class="params">            RecyclerView.State state, <span class="type">boolean</span> stopOnFocusable)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">int</span> <span class="variable">start</span> <span class="operator">=</span> layoutState.mAvailable;</span><br><span class="line">        <span class="keyword">if</span> (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) &#123;</span><br><span class="line">            <span class="comment">// TODO ugly bug fix. should not happen</span></span><br><span class="line">            <span class="keyword">if</span> (layoutState.mAvailable &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                layoutState.mScrollingOffset += layoutState.mAvailable;</span><br><span class="line">            &#125;</span><br><span class="line">            recycleByLayoutState(recycler, layoutState);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>这这段代码中判断了当前是否是滚动触发的fill方法，如果是调用recycleByLayoutState(recycler, layoutState)方法。这个方法几经周转会调用到removeAndRecycleViewAt方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeAndRecycleViewAt</span><span class="params">(<span class="type">int</span> index, Recycler recycler)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">View</span> <span class="variable">view</span> <span class="operator">=</span> getChildAt(index);</span><br><span class="line">        removeViewAt(index);</span><br><span class="line">        recycler.recycleView(view);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>这里注意先把视图remove掉了，而不是detach掉。然后调用Recycler中的recycleView方法，这个方法最后会调用recycleViewHolderInternal方法，方法如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">recycleViewHolderInternal</span><span class="params">(ViewHolder holder)</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (forceRecycle || holder.isRecyclable()) &#123;</span><br><span class="line">                <span class="keyword">if</span> (省略) &#123;</span><br><span class="line">                    <span class="type">int</span> <span class="variable">cachedViewSize</span> <span class="operator">=</span> mCachedViews.size();</span><br><span class="line">                    <span class="keyword">if</span> (cachedViewSize &gt;= mViewCacheMax &amp;&amp; cachedViewSize &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                        recycleCachedViewAt(<span class="number">0</span>);</span><br><span class="line">                        cachedViewSize--;</span><br><span class="line">                    &#125;</span><br><span class="line">                    mCachedViews.add(targetCacheIndex, holder);</span><br><span class="line">                    cached = <span class="literal">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (!cached) &#123;</span><br><span class="line">                    addViewHolderToRecycledViewPool(holder, <span class="literal">true</span>);</span><br><span class="line">                    recycled = <span class="literal">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>删除不相关代码后逻辑很清晰。前面我们说过mCachedViews是有容量限制的，默认为2。那么如果符合放到mCachedViews中的条件，首先会判断mCachedViews是否已经满了，如果满了会通过recycleCachedViewAt(0)方法把最老得那个缓存放进RecyclerPool,然后在把新的视图放进mCachedViews中。如果这个视图不符合条件会直接被放进RecyclerPool中。我们注意到，在缓存进mCachedViews之前，我们的视图只是被remove掉了，绑定的数据等信息都还在，这意味着从mCachedViews取出的视图如果符合需要的目标视图是可以直接展示的，而不需要重新绑定。而放进RecyclerPool最终是要调用putRecycledView方法的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">putRecycledView</span><span class="params">(ViewHolder scrap)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">int</span> <span class="variable">viewType</span> <span class="operator">=</span> scrap.getItemViewType();</span><br><span class="line">        <span class="keyword">final</span> ArrayList&lt;ViewHolder&gt; scrapHeap = getScrapDataForType(viewType).mScrapHeap;</span><br><span class="line">        <span class="keyword">if</span> (mScrap.get(viewType).mMaxScrap &lt;= scrapHeap.size()) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        scrap.resetInternal();</span><br><span class="line">        scrapHeap.add(scrap);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>这个方法中同样对容量做了判断，跟mCachedViews不一样，如果容量满了，就不再继续缓存了。在缓存之前先调用了scrap.resetInternal()方法，这个方法顾名思义是个重置的方法，缓存之前把视图的信息都清除掉了，这也是为什么这里缓存满了之后就不再继续缓存了，而不是把老的缓存替换掉，因为它们重置后都一样了（这里指具有同种itemType的是一样的）。这就是滑动缓存的全过程，至此我们知道了滚动出去的视图去哪了，那么滚动进来的视图哪来的呢？</p><ul><li>和从无到有的过程一样，最后滚动也调用了fill方法，那最后必然是要走到前面分析的获取视图的5个流程。前面说过在布局完成之后，Scrap层的缓存就是空的了，那就只能从mCachedViews或者RecyclerPool中取了，都取不到最后就会走onCreateViewHolder创建视图。到这里滑动时的缓存以及取缓存就讲完了。</li></ul><h3 id="数据更新时的缓存过程"><a href="#数据更新时的缓存过程" class="headerlink" title="数据更新时的缓存过程"></a>数据更新时的缓存过程</h3><p>这块我就简单说一下结论，感兴趣的同学可以自行查看源码。为什么我们在有数据刷新的时候推荐大家使用notifyItemChanged等方法而不使用notifyDataSetChanged方法呢？</p><ul><li>在调用notifyDataSetChanged方法后，所有的子view会被标记，这个标记导致它们最后都被缓存到RecyclerPool中，然后重新绑定数据。并且由于RecyclerPool有容量限制，如果不够最后就要重新创建新的视图了。</li><li>但是使用notifyItemChanged等方法会将视图缓存到mChangedScrap和mAttachedScrap中，这两个缓存是没有容量限制的，所以基本不会重新创建新的视图，只是mChangedScrap中的视图需要重新绑定一下。</li></ul></li></ul><p> 转自：<a href="https://zhuanlan.zhihu.com/p/80475040">https://zhuanlan.zhihu.com/p/80475040</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;strong&gt;A flexible view for providing a limited window into a large data set.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;缓存层级&quot;&gt;&lt;a href=&quot;#缓存层级&quot; class=&quot;headerlin</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="RecyclerView" scheme="https://adewu.github.io/tags/RecyclerView/"/>
    
  </entry>
  
  <entry>
    <title>浅析Handler原理</title>
    <link href="https://adewu.github.io/2016/03/12/handler/"/>
    <id>https://adewu.github.io/2016/03/12/handler/</id>
    <published>2016-03-12T11:22:00.000Z</published>
    <updated>2024-02-11T12:48:38.523Z</updated>
    
    <content type="html"><![CDATA[<p>Android的异步消息处理模型主要是由Handler、Looper、MessageQueue组成的。通过Handler我们很容易的实现在UI线程与其他线程之间的消息传递。</p><h3 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h3><h4 id="Handler"><a href="#Handler" class="headerlink" title="Handler"></a>Handler</h4><p>一个Handler用于发送或者处理与一个线程的消息队列(MessageQueue)相关联的消息或者可执行对象(Runnable)。每个Handler实例跟一个线程以及线程消息队列相关联。当新建一个Handler实例时，该Handler就跟创建它的线程以及线程队列绑定在一起。此后，它就会将消息以及可执行对象传送到消息队列中，并且处理从消息队列中出来的消息。</p><p>Handler有两个用途：（1）对消息以及可执行对象进行调度，以便未来某个时间点执行之；（2）向另一个线程发送一个消息或者执行对象。</p><p>当应用程序进程创建完成后，主线程负责运行一个用于管理应用内的对象，如activities,broadcast receivers的消息队列。用户可以创建自己的线程，通过一个Handler跟主线程进行通信。</p><h4 id="Looper"><a href="#Looper" class="headerlink" title="Looper"></a>Looper</h4><p><em>Looper</em>用于为线程运行一个消息循环；线程默认是没有消息循环的，要建立一个新的Looper,首先需要在线程内调用<code>prepare</code>，然后调用<code>loop</code>进入消息处理。</p><h4 id="MessageQueue"><a href="#MessageQueue" class="headerlink" title="MessageQueue"></a>MessageQueue</h4><p><em>MessageQueue</em>用于保存被<em>looper</em>分发出来的消息。消息并不是直接添加到<em>MessageQueue</em>中的，而是通过跟<em>looper</em>相关联的<em>Handler</em>进行传递。</p><h3 id="消息循环流程"><a href="#消息循环流程" class="headerlink" title="消息循环流程"></a>消息循环流程</h3><p>下面的图展示了这些组件之间的流程。当HandlerThread启动时，HandlerThread.run()会调用Looper.prepare()来初始化Looper，然后调用Looper.loop()。</p><p>在Looper.loop()内部是一个for循环，它始终调用loopOnce()。如果loopOnce()返回false，那么loop()结束for循环。loopOnce()调用MessageQueue.next()来接收下一个待处理的消息。消息的类型是Handler。在接收到消息后，loopOnce()调用Message.target.dispatchMessage()，它再调用Message.target.handleMessage()来处理消息。</p><p>当MessageQueue为空时，next()会被阻塞，因此HandlerThread也会被阻塞。然而，当调用HandlerThread.quitSafely()时，它会清空MessageQueue并将MessageQueue.mQuitting设置为true。此外，如果此时next()被阻塞，它将会被唤醒。当next()检查到mQuitting为true时，它会返回null。当loopOnce()从next()接收到null时，它会返回false给loop()。最后，loop()结束for循环。</p><p><img src="/../imgs/handler_looper_overview.png" alt="alt text" title="handler流程"></p><h3 id="Message的操作"><a href="#Message的操作" class="headerlink" title="Message的操作"></a>Message的操作</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//关键点1</span></span><br><span class="line">Handler handler=<span class="keyword">new</span> <span class="title class_">Handler</span>();</span><br><span class="line"><span class="comment">//关键点2</span></span><br><span class="line">handler.post(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">//TODO </span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>这里有两个点需要注意，先看关键点1，Handler对象的创建，直观来看可能感觉不到有什么注意的地方，但是如果你在普通线程创建Handler，就会遇到异常，因为<strong>普通线程是不能创建Handler对象的，必须是Looper线程才能创建，才有意义</strong>，可以看下其构造函数：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">Handler</span><span class="params">(Callback callback, <span class="type">boolean</span> async)</span> &#123;</span><br><span class="line"></span><br><span class="line">    mLooper = Looper.myLooper();</span><br><span class="line">    <span class="keyword">if</span> (mLooper == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(</span><br><span class="line">            <span class="string">&quot;Can&#x27;t create handler inside thread that has not called Looper.prepare()&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    mQueue = mLooper.mQueue;</span><br><span class="line">    mCallback = callback;</span><br><span class="line">    mAsynchronous = async;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从上面的代码可以看出，Looper.myLooper()必须非空，否则就会抛出 RuntimeException异常，Looper.myLooper()什么时候才会非空？</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="meta">@Nullable</span> Looper <span class="title function_">myLooper</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> sThreadLocal.get();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">prepare</span><span class="params">(<span class="type">boolean</span> quitAllowed)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (sThreadLocal.get() != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;Only one Looper may be created per thread&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    sThreadLocal.set(<span class="keyword">new</span> <span class="title class_">Looper</span>(quitAllowed));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的两个函数牵扯到稍微拧巴的<a href="https://cloud.tencent.com/product/cdcs?from_column=20065&from=20065">数据存储</a>模型，不分析，只要记住只有调用过Looper.prepare的线程，才会生成一个线程单利的Looper对象，Looper.prepare只能调用一次，再次调用会抛出异常。其实prepare的作用就是新建一个Looper对象，而在new Looper对象的时候，会创建关键的消息队列对象：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="title function_">Looper</span><span class="params">(<span class="type">boolean</span> quitAllowed)</span> &#123;</span><br><span class="line">    mQueue = <span class="keyword">new</span> <span class="title class_">MessageQueue</span>(quitAllowed);</span><br><span class="line">    mThread = Thread.currentThread();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>之后，一个线程就有了MessageQueue，虽然还没有调用Loop.loop()将线程变成loop线程，但是new Handler已经没问题。接着看handler.post函数，它将会创建一个Message(如果需要)，并将Message插入到MessageQueue，供loop线程摘取并执行。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">   <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">post</span><span class="params">(Runnable r)</span></span><br><span class="line">    &#123;</span><br><span class="line">       <span class="keyword">return</span>  sendMessageDelayed(getPostMessage(r), <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> Message <span class="title function_">getPostMessage</span><span class="params">(Runnable r)</span> &#123;</span><br><span class="line">    <span class="type">Message</span> <span class="variable">m</span> <span class="operator">=</span> Message.obtain();</span><br><span class="line">    m.callback = r;</span><br><span class="line">    <span class="keyword">return</span> m;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 静态方法，同步</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Message <span class="title function_">obtain</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (sPoolSync) &#123;</span><br><span class="line">        <span class="keyword">if</span> (sPool != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">Message</span> <span class="variable">m</span> <span class="operator">=</span> sPool;</span><br><span class="line">            sPool = m.next;</span><br><span class="line">            m.next = <span class="literal">null</span>;</span><br><span class="line">            m.flags = <span class="number">0</span>; <span class="comment">// clear in-use flag</span></span><br><span class="line">            sPoolSize--;</span><br><span class="line">            <span class="keyword">return</span> m;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Message</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>mAsynchronous可以先不关心，我们使用的一般是mAsynchronous&#x3D;false的，可以看到，Handler最后通过MessageQueue的enqueueMessage函数来进行插入，</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">boolean</span> <span class="title function_">enqueueMessage</span><span class="params">(Message msg, <span class="type">long</span> when)</span>  &#123;</span><br><span class="line">        <span class="keyword">if</span> (msg.target == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Message must have a target.&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (msg.isInUse()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(msg + <span class="string">&quot; This message is already in use.&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">      <span class="comment">// 需要同步</span></span><br><span class="line">    <span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;</span><br><span class="line">        msg.markInUse();</span><br><span class="line">        msg.when = when;</span><br><span class="line">        <span class="type">Message</span> <span class="variable">p</span> <span class="operator">=</span> mMessages;</span><br><span class="line">        <span class="type">boolean</span> needWake;</span><br><span class="line">        <span class="keyword">if</span> (p == <span class="literal">null</span> || when == <span class="number">0</span> || when &lt; p.when) &#123;</span><br><span class="line">        &lt;!--关键点<span class="number">1</span>--&gt;</span><br><span class="line">            msg.next = p;</span><br><span class="line">            mMessages = msg;</span><br><span class="line">            needWake = mBlocked;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        &lt;!--关键点<span class="number">2</span>--&gt;</span><br><span class="line">            needWake = mBlocked &amp;&amp; p.target == <span class="literal">null</span> &amp;&amp; msg.isAsynchronous();</span><br><span class="line">            Message prev;</span><br><span class="line">            <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">                prev = p;</span><br><span class="line">                p = p.next;</span><br><span class="line">                <span class="keyword">if</span> (p == <span class="literal">null</span> || when &lt; p.when) &#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (needWake &amp;&amp; p.isAsynchronous()) &#123;</span><br><span class="line">                    needWake = <span class="literal">false</span>;</span><br><span class="line">                &#125;&#125;</span><br><span class="line">            msg.next = p; <span class="comment">// invariant: p == prev.next</span></span><br><span class="line">            prev.next = msg;</span><br><span class="line">        &#125;</span><br><span class="line">        &lt;!--关键点<span class="number">3</span>--&gt;</span><br><span class="line">        <span class="keyword">if</span> (needWake) &#123;</span><br><span class="line">            nativeWake(mPtr);</span><br><span class="line">        &#125; &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>; &#125;</span><br></pre></td></tr></table></figure><p>很明显enqueueMessage需要同步,因为存在多个线程往一个Loop线程的MessageQueue中插入消息的场景。 这里其实是将Message根据延时插入到特定的地方，先看下关键点1，mMessages其实代表消息队列的头部，如果mMessages为空，说明还没有消息，如果当前插入的消息不需要延时，或者说延时比mMessages头消息的延时要小，那么当前要插入的消息就需要放在头部，至于是否需要唤醒队列，则需要根据当前的Loop线程的状态来判断，后面讲Loop线程的时候再回过头说；再来看下关键点2，这个时候需要将消息插入到队列中间，其实就是找到第一个Delay事件小于当前Message的非空Message，并插入到它的前面，往队列中插入消息时，如果Loop线程在睡眠，是不应该唤醒的，异步消息的处理会更加特殊一些，先不讨论。最后看关键点3，如果需要唤醒Loop线程，通过nativeWake唤醒，以上，普通消息的插入算结束了，接下来看一下消息的执行。</p><h3 id="MessageQueue中Message消息的执行"><a href="#MessageQueue中Message消息的执行" class="headerlink" title="MessageQueue中Message消息的执行"></a>MessageQueue中Message消息的执行</h3><p>在消息的发送部分已经消息模型的两个必要条件：消息队里+互斥机制，接下来看一下其他两个条件，Loop线程+消费者模型的同步机制。MessageQueue只有同Loop线程（死循环线程）配合起来才有意义，普通线程必须可以通过Looper的loop函数变成Loop线程，loop函数除了是个死循环，还包含了从MessageQueue摘取消息并执行的逻辑。看一下这个函数：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">loop</span><span class="params">()</span> &#123;</span><br><span class="line">  `&lt;!--关键点<span class="number">1</span> 确保MessageQueue准备好--&gt;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">Looper</span> <span class="variable">me</span> <span class="operator">=</span> myLooper();</span><br><span class="line">    <span class="keyword">if</span> (me == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;No Looper; Looper.prepare() wasn&#x27;t called on this thread.&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    &lt;!--关键点<span class="number">2</span>--&gt;</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">    &lt;!--关键点<span class="number">3</span> 获取一个消息，如果队列为空，阻塞等待--&gt;</span><br><span class="line">        <span class="type">Message</span> <span class="variable">msg</span> <span class="operator">=</span> queue.next(); <span class="comment">// might block</span></span><br><span class="line">        <span class="keyword">if</span> (msg == <span class="literal">null</span>) &#123;</span><br><span class="line">          <span class="comment">// No message indicates that the message queue is quitting.</span></span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &lt;!--关键点<span class="number">4</span> 执行消息回调--&gt;</span><br><span class="line">        msg.target.dispatchMessage(msg);</span><br><span class="line">    ...</span><br><span class="line">    &lt;!--关键点<span class="number">5</span> 清理，回收到缓存池--&gt;</span><br><span class="line">        msg.recycleUnchecked();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>先看下关键点1，它要确保当前线程已经调用过Looper.prepare函数，并且准备好了MessageQueue消息队列；再看关键点2，其实就是将线程化身成Looper线程，变成死循环，不断的读取执行消息；关键点3，就是从MessageQueue摘取消息的函数，如果当前消息队列上没有消息，Loop线程就会进入阻塞，直到其他线程插入消息，唤醒当前线程。如果消息读取成功，就走到关键点4，执行target对象的回调函数，执行完毕，进入关键点5，回收清理Message对象，放入Message缓存池。直接看关键点3，消息的摘取与阻塞：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line">Message <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">     </span><br><span class="line">     <span class="type">int</span> <span class="variable">pendingIdleHandlerCount</span> <span class="operator">=</span> -<span class="number">1</span>; <span class="comment">// -1 only during first iteration</span></span><br><span class="line">     <span class="type">int</span> <span class="variable">nextPollTimeoutMillis</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">     <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        &lt;!--关键点<span class="number">1</span> 是否需要阻塞等待，第一次一定不阻塞--&gt;</span><br><span class="line">         nativePollOnce(ptr, nextPollTimeoutMillis);</span><br><span class="line">        &lt;!--关键点<span class="number">2</span> 同步互斥--&gt;</span><br><span class="line">         <span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;</span><br><span class="line">             <span class="keyword">final</span> <span class="type">long</span> <span class="variable">now</span> <span class="operator">=</span> SystemClock.uptimeMillis();</span><br><span class="line">             <span class="type">Message</span> <span class="variable">prevMsg</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">             <span class="type">Message</span> <span class="variable">msg</span> <span class="operator">=</span> mMessages;</span><br><span class="line">       &lt;!--关键点<span class="number">3</span> 是否存在barier--&gt;</span><br><span class="line">             <span class="keyword">if</span> (msg != <span class="literal">null</span> &amp;&amp; msg.target == <span class="literal">null</span>) &#123;</span><br><span class="line">                 <span class="keyword">do</span> &#123;</span><br><span class="line">                     prevMsg = msg;</span><br><span class="line">                     msg = msg.next;</span><br><span class="line">                 &#125; <span class="keyword">while</span> (msg != <span class="literal">null</span> &amp;&amp; !msg.isAsynchronous());</span><br><span class="line">             &#125;</span><br><span class="line">        &lt;!--关键点<span class="number">4</span> 第一个消息是否需要阻塞等待，并计算出阻塞等待时间--&gt;</span><br><span class="line">             <span class="keyword">if</span> (msg != <span class="literal">null</span>) &#123;</span><br><span class="line">                 <span class="keyword">if</span> (now &lt; msg.when) &#123;</span><br><span class="line">                     <span class="comment">// Next message is not ready.  Set a timeout to wake up when it is ready.</span></span><br><span class="line">                     nextPollTimeoutMillis = (<span class="type">int</span>) Math.min(msg.when - now, Integer.MAX_VALUE);</span><br><span class="line">                 &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                     <span class="comment">// Got a message.</span></span><br><span class="line">                     mBlocked = <span class="literal">false</span>;</span><br><span class="line">                     <span class="keyword">if</span> (prevMsg != <span class="literal">null</span>) &#123;</span><br><span class="line">                         prevMsg.next = msg.next;</span><br><span class="line">                     &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                         mMessages = msg.next;</span><br><span class="line">                     &#125;</span><br><span class="line">                     msg.next = <span class="literal">null</span>;</span><br><span class="line">                     msg.markInUse();</span><br><span class="line">                     <span class="keyword">return</span> msg;</span><br><span class="line">                 &#125;</span><br><span class="line">             &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        &lt;!--关键点<span class="number">5</span> 需要无限等待--&gt;</span><br><span class="line">                 nextPollTimeoutMillis = -<span class="number">1</span>;</span><br><span class="line">             &#125;         </span><br><span class="line">       &lt;!--关键点<span class="number">6</span> 没有可以即刻执行的Message，查看是否存在需要处理的IdleHandler，如果不存在，则返回，阻塞等待，如果存在则执行IdleHandler--&gt;</span><br><span class="line">             <span class="keyword">if</span> (pendingIdleHandlerCount &lt; <span class="number">0</span></span><br><span class="line">                     &amp;&amp; (mMessages == <span class="literal">null</span> || now &lt; mMessages.when)) &#123;</span><br><span class="line">                 pendingIdleHandlerCount = mIdleHandlers.size();</span><br><span class="line">             &#125;</span><br><span class="line">             <span class="keyword">if</span> (pendingIdleHandlerCount &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                 <span class="comment">// No idle handlers to run.  Loop and wait some more.</span></span><br><span class="line">                 mBlocked = <span class="literal">true</span>;</span><br><span class="line">                 <span class="keyword">continue</span>;</span><br><span class="line">             &#125;</span><br><span class="line">             <span class="keyword">if</span> (mPendingIdleHandlers == <span class="literal">null</span>) &#123;</span><br><span class="line">                 mPendingIdleHandlers = <span class="keyword">new</span> <span class="title class_">IdleHandler</span>[Math.max(pendingIdleHandlerCount, <span class="number">4</span>)];</span><br><span class="line">             &#125;</span><br><span class="line">             mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);</span><br><span class="line">         &#125;</span><br><span class="line">         &lt;!--关键点<span class="number">7</span>处理IdleHandler--&gt;</span><br><span class="line">         <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; pendingIdleHandlerCount; i++) &#123;</span><br><span class="line">             <span class="keyword">final</span> <span class="type">IdleHandler</span> <span class="variable">idler</span> <span class="operator">=</span> mPendingIdleHandlers[i];</span><br><span class="line">             mPendingIdleHandlers[i] = <span class="literal">null</span>; <span class="comment">// release the reference to the handler</span></span><br><span class="line">             <span class="type">boolean</span> <span class="variable">keep</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">             <span class="keyword">try</span> &#123;</span><br><span class="line">                 keep = idler.queueIdle();</span><br><span class="line">             &#125; <span class="keyword">catch</span> (Throwable t) &#123;</span><br><span class="line">                 Log.wtf(TAG, <span class="string">&quot;IdleHandler threw exception&quot;</span>, t);</span><br><span class="line">             &#125;</span><br><span class="line">             <span class="keyword">if</span> (!keep) &#123;</span><br><span class="line">                 <span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;</span><br><span class="line">                     mIdleHandlers.remove(idler);</span><br><span class="line">                 &#125;</span><br><span class="line">             &#125;</span><br><span class="line">         &#125;</span><br><span class="line">        &lt;!--处理完IdleHandler ，需要重新判断Message队列 nextPollTimeoutMillis赋值为<span class="number">0</span>--&gt;</span><br><span class="line">         pendingIdleHandlerCount = <span class="number">0</span>;</span><br><span class="line">         nextPollTimeoutMillis = <span class="number">0</span>;</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>先看下关键点1 nativePollOnce，这是个native函数，其主要作用是设置一个定时的睡眠，其参数timeoutMillis，不同的值意义不同</p><ul><li>timeoutMillis &#x3D;0 ：无需睡眠，直接返回</li><li>timeoutMillis &gt;0 ：睡眠如果超过timeoutMillis，就返回</li><li>timeoutMillis &#x3D;-1：一直睡眠，知道其他线程唤醒它</li></ul><p>next函数中，nextPollTimeoutMillis初始值&#x3D;0 ，所以for循环第一次是一定不会阻塞的，如果能找到一个Delay倒计时结束的消息，就返回该消息，否则，执行第二次循环，睡眠等待，直到头部第一个消息Delay时间结束，所以next函数一定会返回一个Message对象。再看MessageQueue的nativePollOnce函数之前，先走通整个流程，接着看关键点2，这里其实是牵扯到一个互斥的问题，防止多个线程同时从消息队列取消息，关键点3主要是看看是否需要处理异步消息，关键点4，是常用的入口，看取到的消息是不是需要立即执行，需要立即执行的就返回当前消息，如果需要等待，计算出等待时间。最后，如果需要等待，还要查看，IdleHandler列表是否为空，不为空的话，需要处理IdleHandler列表，最后，重新计算一遍。</p><p>接着分析nativePollOnce函数，该函数可以看做睡眠阻塞的入口，该函数是一个native函数，牵扯到native层的Looper与MessageQueue，因为java层的MessageQueue只是一个简单的类，没有处理睡眠与唤醒的机制，首先看一下Java层MessageQueue构造函数，这里牵扯到后面的线程阻塞原理：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">MessageQueue</span>(boolean quitAllowed) &#123;</span><br><span class="line">    mQuitAllowed = quitAllowed;</span><br><span class="line">    mPtr = <span class="built_in">nativeInit</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>MessageQueue的nativeInit函数在Native层创建了NativeMessageQueue与Looper，不过<strong>对于Java层来说，Native层的NativeMessageQueue只用来处理线程的睡眠与唤醒，Java层发送的消息还是在Java层被处理</strong>：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">static</span> jlong <span class="title">android_os_MessageQueue_nativeInit</span><span class="params">(JNIEnv* env, jclass clazz)</span> </span>&#123;</span><br><span class="line">    NativeMessageQueue* nativeMessageQueue = <span class="keyword">new</span> <span class="built_in">NativeMessageQueue</span>();</span><br><span class="line">    <span class="keyword">if</span> (!nativeMessageQueue) &#123;</span><br><span class="line">        <span class="built_in">jniThrowRuntimeException</span>(env, <span class="string">&quot;Unable to allocate native queue&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    nativeMessageQueue-&gt;<span class="built_in">incStrong</span>(env);</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">reinterpret_cast</span>&lt;jlong&gt;(nativeMessageQueue);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">NativeMessageQueue::<span class="built_in">NativeMessageQueue</span>() :</span><br><span class="line">        <span class="built_in">mPollEnv</span>(<span class="literal">NULL</span>), <span class="built_in">mPollObj</span>(<span class="literal">NULL</span>), <span class="built_in">mExceptionObj</span>(<span class="literal">NULL</span>) &#123;</span><br><span class="line">    mLooper = Looper::<span class="built_in">getForThread</span>();</span><br><span class="line">    <span class="keyword">if</span> (mLooper == <span class="literal">NULL</span>) &#123;</span><br><span class="line">        mLooper = <span class="keyword">new</span> <span class="built_in">Looper</span>(<span class="literal">false</span>);</span><br><span class="line">        Looper::<span class="built_in">setForThread</span>(mLooper);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Looper::<span class="built_in">Looper</span>(<span class="type">bool</span> allowNonCallbacks) :</span><br><span class="line">        <span class="built_in">mAllowNonCallbacks</span>(allowNonCallbacks), <span class="built_in">mSendingMessage</span>(<span class="literal">false</span>),</span><br><span class="line">        <span class="built_in">mPolling</span>(<span class="literal">false</span>), <span class="built_in">mEpollFd</span>(<span class="number">-1</span>), <span class="built_in">mEpollRebuildRequired</span>(<span class="literal">false</span>),</span><br><span class="line">        <span class="built_in">mNextRequestSeq</span>(<span class="number">0</span>), <span class="built_in">mResponseIndex</span>(<span class="number">0</span>), <span class="built_in">mNextMessageUptime</span>(LLONG_MAX) &#123;</span><br><span class="line">    &lt;!--关键点<span class="number">1</span>--&gt;</span><br><span class="line">    &lt;!-- eventfd 这个函数会创建一个 事件对象 老版本用管道来实现--&gt;</span><br><span class="line">    mWakeEventFd = <span class="built_in">eventfd</span>(<span class="number">0</span>, EFD_NONBLOCK | EFD_CLOEXEC);</span><br><span class="line"></span><br><span class="line">    AutoMutex _l(mLock);</span><br><span class="line">    <span class="built_in">rebuildEpollLocked</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Looper::rebuildEpollLocked</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">if</span> (mEpollFd &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="built_in">close</span>(mEpollFd);</span><br><span class="line">&#125;</span><br><span class="line">mEpollFd = <span class="built_in">epoll_create</span>(EPOLL_SIZE_HINT);</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">epoll_event</span> eventItem;</span><br><span class="line"><span class="built_in">memset</span>(&amp; eventItem, <span class="number">0</span>, <span class="built_in">sizeof</span>(epoll_event)); <span class="comment">// zero out unused members of data field union</span></span><br><span class="line">eventItem.events = EPOLLIN;</span><br><span class="line">eventItem.data.fd = mWakeEventFd;</span><br><span class="line"><span class="type">int</span> result = <span class="built_in">epoll_ctl</span>(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, &amp; eventItem);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; mRequests.<span class="built_in">size</span>(); i++) &#123;</span><br><span class="line">    <span class="type">const</span> Request&amp; request = mRequests.<span class="built_in">valueAt</span>(i);</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">epoll_event</span> eventItem;</span><br><span class="line">    request.<span class="built_in">initEventItem</span>(&amp;eventItem);</span><br><span class="line">    <span class="type">int</span> epollResult = <span class="built_in">epoll_ctl</span>(mEpollFd, EPOLL_CTL_ADD, request.fd, &amp; eventItem);</span><br><span class="line">    <span class="keyword">if</span> (epollResult &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="built_in">ALOGE</span>(<span class="string">&quot;Error adding epoll events for fd %d while rebuilding epoll set: %s&quot;</span>,</span><br><span class="line">              request.fd, <span class="built_in">strerror</span>(errno));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>看一下关键点1，这里其实是采用了Linux的新API，这里用的是7.0的源码，eventfd函数会创建一个eventfd，这是一个计数器相关的fd，计数器不为零是有可读事件发生，read以后计数器清零，write递增计数器；返回的fd可以进行如下操作：read、write、select(poll、epoll)、close，现在我们知道了，Native层有也有一套MessageQueue与Looper，简单看一下Java层如何使用Native层对象的，接着走nativePollOnce</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">android_os_MessageQueue_nativePollOnce</span><span class="params">(JNIEnv* env, jobject obj,</span></span></span><br><span class="line"><span class="params"><span class="function">        jlong ptr, jint timeoutMillis)</span> </span>&#123;</span><br><span class="line">    NativeMessageQueue* nativeMessageQueue = <span class="built_in">reinterpret_cast</span>&lt;NativeMessageQueue*&gt;(ptr);</span><br><span class="line">    nativeMessageQueue-&gt;<span class="built_in">pollOnce</span>(env, obj, timeoutMillis);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">NativeMessageQueue::pollOnce</span><span class="params">(JNIEnv* env, jobject pollObj, <span class="type">int</span> timeoutMillis)</span> </span>&#123;</span><br><span class="line">    mPollEnv = env;</span><br><span class="line">    mPollObj = pollObj;</span><br><span class="line">    mLooper-&gt;<span class="built_in">pollOnce</span>(timeoutMillis);</span><br><span class="line">    mPollObj = <span class="literal">NULL</span>;</span><br><span class="line">    mPollEnv = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>所以最终调用Looper::pollOnce，Java层有自己的消息队列，pollOnce也没有更新Java层对象，那么Native层的消息队里对于Java层有什么用呢，其实<strong>只有睡眠与唤醒的作用</strong>，比如2.3之前的版本，Native层的MessageQueue都不具备发送消息的能力。不过后来Native添加了发送消息的功能，但是日常开发我们用不到，不过如果native层如果有消息，一定会优先执行native层的消息</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">Looper::pollOnce</span><span class="params">(<span class="type">int</span> timeoutMillis, <span class="type">int</span>* outFd, <span class="type">int</span>* outEvents, <span class="type">void</span>** outData)</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> result = <span class="number">0</span>;</span><br><span class="line">         ...</span><br><span class="line">        result = <span class="built_in">pollInner</span>(timeoutMillis);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>pollInner   函数比较长，主要是通过利用epoll_wait监听上面的管道或者eventfd，等待超时或者其他线程的唤醒，不过多分析</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">Looper::pollInner</span><span class="params">(<span class="type">int</span> timeoutMillis)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  mPolling = <span class="literal">true</span>;</span><br><span class="line">   &lt;!--关键点<span class="number">1</span>--&gt;</span><br><span class="line">   <span class="keyword">struct</span> <span class="title class_">epoll_event</span> eventItems[EPOLL_MAX_EVENTS];</span><br><span class="line">   <span class="type">int</span> eventCount = <span class="built_in">epoll_wait</span>(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);</span><br><span class="line">    &lt;!--关键点<span class="number">2</span>--&gt;</span><br><span class="line">   mPolling = <span class="literal">false</span>;</span><br><span class="line">   mLock.<span class="built_in">lock</span>();</span><br><span class="line">    &lt;!--关键点<span class="number">3</span> 查看那个fd上又写入操作--&gt;             <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; eventCount; i++) &#123;</span><br><span class="line">       <span class="type">int</span> fd = eventItems[i].data.fd;</span><br><span class="line">       <span class="type">uint32_t</span> epollEvents = eventItems[i].events;</span><br><span class="line">       &lt;!--关键点<span class="number">5</span> 唤醒fd 上有写入操作 返回Java层继续执行--&gt;</span><br><span class="line">       <span class="keyword">if</span> (fd == mWakeEventFd) &#123;</span><br><span class="line">           <span class="keyword">if</span> (epollEvents &amp; EPOLLIN) &#123;</span><br><span class="line">               <span class="built_in">awoken</span>();</span><br><span class="line">           &#125; <span class="keyword">else</span> &#123; &#125; &#125; </span><br><span class="line">           <span class="keyword">else</span> &#123;</span><br><span class="line">         &lt;!--关键点<span class="number">6</span> 本地MessageQueue有消息，执行本地消息--&gt;    </span><br><span class="line">           &#125; &#125;</span><br></pre></td></tr></table></figure><p>以上牵扯到Linux中的<a href="https://cloud.tencent.com/developer/tools/blog-entry?target=https://link.jianshu.com/?t=http://blog.csdn.net/yusiguyuan/article/details/15027821&source=article&objectId=1154085">epoll机制：epoll_create、epoll_ctl、epoll_wait、close等</a>， <strong>用一句话概括：线程阻塞监听多个fd句柄，其中一个fd有写入操作，当前线程就被唤醒</strong>。这里不用太过于纠结，只要理解，这是线程间通信的一种方式，为了处理多线程间生产者与消费者通信模型用的，看下7.0源码中native层实现的同步逻辑：</p><p><img src="/../imgs/handler_java_native.jpeg" alt="alt text" title="Looper_Java层与native层关系"></p><p>在更早的Android版本中，同步逻辑是利用管道通信实现的,不过思想是一致的，看一下4.3的代码</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">Looper::<span class="built_in">Looper</span>(<span class="type">bool</span> allowNonCallbacks) :</span><br><span class="line">        <span class="built_in">mAllowNonCallbacks</span>(allowNonCallbacks), <span class="built_in">mSendingMessage</span>(<span class="literal">false</span>),</span><br><span class="line">        <span class="built_in">mResponseIndex</span>(<span class="number">0</span>), <span class="built_in">mNextMessageUptime</span>(LLONG_MAX) &#123;</span><br><span class="line">    <span class="type">int</span> wakeFds[<span class="number">2</span>];</span><br><span class="line">    <span class="type">int</span> result = <span class="built_in">pipe</span>(wakeFds);</span><br><span class="line">    mWakeReadPipeFd = wakeFds[<span class="number">0</span>];</span><br><span class="line">    mWakeWritePipeFd = wakeFds[<span class="number">1</span>];</span><br><span class="line">    result = <span class="built_in">fcntl</span>(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);</span><br><span class="line">    result = <span class="built_in">fcntl</span>(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);</span><br><span class="line">    <span class="comment">// Allocate the epoll instance and register the wake pipe.</span></span><br><span class="line">    mEpollFd = <span class="built_in">epoll_create</span>(EPOLL_SIZE_HINT);</span><br><span class="line">    <span class="built_in">LOG_ALWAYS_FATAL_IF</span>(mEpollFd &lt; <span class="number">0</span>, <span class="string">&quot;Could not create epoll instance.  errno=%d&quot;</span>, errno);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">epoll_event</span> eventItem;</span><br><span class="line">    <span class="built_in">memset</span>(&amp; eventItem, <span class="number">0</span>, <span class="built_in">sizeof</span>(epoll_event)); <span class="comment">// zero out unused members of data field union</span></span><br><span class="line">    eventItem.events = EPOLLIN;</span><br><span class="line">    eventItem.data.fd = mWakeReadPipeFd;</span><br><span class="line">    result = <span class="built_in">epoll_ctl</span>(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &amp; eventItem);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>总结：</p><ul><li>loop线程睡眠的原理 ：在MessageQueue中找到下一个需要执行的消息，没有消息的话，需要无限睡眠等待其他线程插入消息唤醒，如果有消息，计算出执行下一个消息需要等待的时间，阻塞等待，直到超时。</li><li>Java层与Native层两份消息队列：Java层的主要是为了业务逻辑，native层，主要为了睡眠与唤醒</li><li>睡眠与唤醒的实现手段：早期版本通过管道，后来如6.0、7.0的版本，是通过eventfd来实现，思想一致。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Android的异步消息处理模型主要是由Handler、Looper、MessageQueue组成的。通过Handler我们很容易的实现在UI线程与其他线程之间的消息传递。&lt;/p&gt;
&lt;h3 id=&quot;基本概念&quot;&gt;&lt;a href=&quot;#基本概念&quot; class=&quot;headerlin</summary>
      
    
    
    
    <category term="Android" scheme="https://adewu.github.io/categories/Android/"/>
    
    
    <category term="Handler" scheme="https://adewu.github.io/tags/Handler/"/>
    
  </entry>
  
  <entry>
    <title>浅谈JVM原理</title>
    <link href="https://adewu.github.io/2016/03/07/jvm/"/>
    <id>https://adewu.github.io/2016/03/07/jvm/</id>
    <published>2016-03-07T10:12:00.000Z</published>
    <updated>2024-02-11T12:48:29.289Z</updated>
    
    <content type="html"><![CDATA[<h3 id="JVM运行时内存区域"><a href="#JVM运行时内存区域" class="headerlink" title="JVM运行时内存区域"></a>JVM运行时内存区域</h3><p>JVM 管理的内存被划分为以下运行时数据区域：程序计数器、虚拟机栈、本地方法栈、堆、方法区。</p><p><img src="/../imgs/jvm_runtime_data_areas.png" alt="alt text" title="jvm内存区域"></p><p> 这6块区域按是否被线程共享，可以分为二大类：</p><p>一类是每个线程所独享的：</p><ol><li><p>PC Register：也称为程序计数器， 记录每个线程当前执行的指令信息(eg:当前执行到哪一条指令，下一条该取哪条指令)</p></li><li><p>JVM Stack： 也称为虚拟机栈，记录每个栈帧(Frame)中的局部变量、方法返回地址等。它的结构如下：</p></li></ol><p><img src="/../imgs/jvm_frame.png" alt="alt text" title="jvm内存区域"></p><p>线程中每次有方法调用时，会创建Frame，方法调用结束时Frame销毁。</p><ol start="3"><li>Native Method Stack: 本地(原生)方法栈，顾名思义就是调用操作系统原生本地方法时，所需要的内存区域。</li></ol><p>上述3类区域，生命周期与Thread相同，即：线程创建时，相应的内存区创建，线程销毁时，释放相应内存。</p><p>另一类是所有线程共享的：</p><ol><li>Heap：即鼎鼎大名的堆内存区，也是GC垃圾回收的主站场，用于存放类的实例对象及Arrays实例等。</li></ol><p>注：Heap被所有线程共享，如果严格意义上抠字眼的话，也不完正确，事实上，由于TLAB的存在，为了防止并发对象分配时，多个对象分配到同1块内存，heap中的TLAB区域，在分配时，是被线程独占写入的。</p><ol start="2"><li><p>Method Area：方法区，主要存放类结构、类成员定义，static静态成员等。</p></li><li><p>Runtime Constant Pool：运行时常量池，比如：字符串，int -128~127范围的值等，它是Method Area中的一部分。</p></li></ol><p>Heap、Method Area 都是在虚拟机启动时创建，虚拟机退出时释放。</p><h3 id="GC垃圾回收原理"><a href="#GC垃圾回收原理" class="headerlink" title="GC垃圾回收原理"></a>GC垃圾回收原理</h3><h4 id="判断一个对象是否该被回收"><a href="#判断一个对象是否该被回收" class="headerlink" title="判断一个对象是否该被回收"></a>判断一个对象是否该被回收</h4><p><img src="/../imgs/jvm_reference_counter.png" alt="alt text" title="jvm内存区域"></p><p>引用计数法，思路很简单，但是如果出现循环引用，即：A引用B，B又引用A，这种情况下就不好办了，所以JVM中使用了另一种称为“可达性分析”的判断方法：</p><p><img src="/../imgs/jvm_reachadble_analytics.png" alt="alt text" title="jvm内存区域"></p><p>还是刚才的循环引用问题，如果A引用B，B又引用A，这2个对象是否能被GC回收？ 答案：关键不是在于A,B之间是否有引用，而是A,B是否可以一直向上追溯到GC Roots。如果与GC Roots没有关联，则会被回收，否则将继续存活。</p><p><img src="/../imgs/jvm_non_reachable_obj.png" alt="alt text" title="jvm内存区域"></p><p>上图是一个用“可达性分析”标记垃圾对象的示例图，灰色的对象表示不可达对象，将等待回收。</p><h4 id="哪些内存区域需要GC"><a href="#哪些内存区域需要GC" class="headerlink" title="哪些内存区域需要GC"></a>哪些内存区域需要GC</h4><p><img src="/../imgs/jvm_areas_need_gc.png" alt="alt text" title="jvm内存区域"></p><p>在上文中，我们知道了thread独享的区域：PC Regiester、JVM Stack、Native Method Stack，其生命周期都与线程相同（即：与线程共生死），所以无需GC。线程共享的Heap区、Method Area则是GC关注的重点对象。</p><h4 id="常用的GC算法"><a href="#常用的GC算法" class="headerlink" title="常用的GC算法"></a>常用的GC算法</h4><ul><li>mark-sweep 标记清除法：标记出来后直接清空。该方法很简单快速，但是缺点也很明显，会产生很多内存碎片。</li><li>mark-copy 标记复制法：将内存对半分，总是保留一块空着，将左侧存活的对象复制到右侧，然后左侧全部清空。避免了内存碎片问题，但是内存浪费很严重，相当于只能使用50%的内存。</li><li>mark-compact 标记-整理(也称标记-压缩)法：避免了上述二种算法的缺点，将垃圾对象清理掉后，同时将剩下的存活对象进行整理挪动（类似于windows的磁盘碎片整理），保证它们占用的空间连续，这样就避免了内存碎片问题，但是整理过程也会降低GC的效率。</li><li>generation-collect <a href="https://samk238.github.io/blogger/GCandDumps-HeapGen.htm">分代收集算法</a>：将内存分成了三大块： 年青代(Young Genaration），老年代(Old Generation）,永久代(Permanent Generation），其中Young Genaration更是又细为分eden，S0, S1三个区。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;JVM运行时内存区域&quot;&gt;&lt;a href=&quot;#JVM运行时内存区域&quot; class=&quot;headerlink&quot; title=&quot;JVM运行时内存区域&quot;&gt;&lt;/a&gt;JVM运行时内存区域&lt;/h3&gt;&lt;p&gt;JVM 管理的内存被划分为以下运行时数据区域：程序计数器、虚拟机栈、本地方法</summary>
      
    
    
    
    <category term="Java" scheme="https://adewu.github.io/categories/Java/"/>
    
    
    <category term="jvm" scheme="https://adewu.github.io/tags/jvm/"/>
    
  </entry>
  
  <entry>
    <title>详解Java类加载机制</title>
    <link href="https://adewu.github.io/2016/01/13/load_java_class/"/>
    <id>https://adewu.github.io/2016/01/13/load_java_class/</id>
    <published>2016-01-13T03:34:00.000Z</published>
    <updated>2024-02-11T12:48:10.903Z</updated>
    
    <content type="html"><![CDATA[<p>Java 虚拟机的启动是通过引导类加载器 ( Bootstrap Class Loader ) 创建一个初始类 (Initial Class ) 来完成，这个类是由虚拟机的具体实现指定。紧接着，Java虚拟机链接这个初始类，初始化并调用它的 public void main(String[])方法。之后的整个执行过程都是由对此方法的调用开始。执行 main 方法中的 Java 虚拟机指令可能会导致 Java 虚拟机链接另外的一些类或接口，也可能会调用另外的方法。</p><p>可能在某种 Java 虚拟机的实现上，初始类会作为命令行参数被提供给虚拟机。当然，虚拟机实现也可以利用一个初始类让类加载器依次加载整个应用。初始类当然也可以选择组合上述的方式来工作。</p><h3 id="类的加载流程"><a href="#类的加载流程" class="headerlink" title="类的加载流程"></a>类的加载流程</h3><p>当 Java 虚拟机将 Java 源码编译为字节码之后，虚拟机便可以将字节码读取进内存，从而进行解析、运行等整个过程，这个过程我们叫：Java 虚拟机的类加载机制。JVM 虚拟机执行 class 字节码的过程可以分为七个阶段：<strong>加载、验证、准备、解析、初始化、使用、卸载。</strong>其中加载、检验、准备、初始化和卸载这个五个阶段的顺序是固定的，而解析则未必。为了支持动态绑定，解析这个过程可以发生在初始化阶段之后。</p><p><img src="/../imgs/class_load_process.webp" alt="alt text" title="类加载过程"></p><h3 id="加载"><a href="#加载" class="headerlink" title="加载"></a>加载</h3><p>什么情况下需要开始<strong>类加载</strong>的第一个阶段：加载。 JAVA虚拟机规范并没有进行强制约束，交给虚拟机的具体实现自由把握。</p><p>加载阶段是“类加载”过程中的一个阶段，这个阶段通常也被称作“装载”，在加载阶段，虚拟机主要完成以下3件事情：</p><ol><li>通过 “类全名” 来获取定义此类的二进制字节流</li><li>将字节流所代表的静态存储结构转换为方法区的运行时数据结构</li><li>在 java 堆中生成一个代表这个类的 java.lang.Class 对象，作为方法区这些数据的访问入口（所以我们能够通过低调用类.getClass() ）</li></ol><p>注意这里字节流不一定非得要从一个 Class 文件获取，这里既可以从 ZIP 包中读取（比如从 jar 包和 war 包中读取），也可以在运行时计算生成（动态代理），也可以由其它文件生成（比如将 JSP 文件转换成对应的 Class 类）。加载的信息存储在 JVM 的方法区。</p><p>对于数组类来说，它并没有对应的字节流，而是由 Java 虚拟机直接生成的。对于其它的类来说，Java 虚拟机则需要借助类加载器来完成查找字节流的过程。</p><p>如果上面那么多记不住： 请一定记住这句： <strong>加载阶段也就是查找获取类的二进制数据（磁盘或者网络）动作，将类的数据（Class 的信息：类的定义或者结构）放入方法区 （内存）</strong>。</p><p>一图说明：</p><p><img src="/../imgs/class_load.png" alt="alt text" title="类加载"></p><h3 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h3><p>验证的主要作用就是确保被加载的类的正确性。也是连接阶段的第一步。说白了也就是我们加载好的 .class 文件不能对我们的虚拟机有危害，所以先检测验证一下。他主要是完成四个阶段的验证：</p><ol><li><strong>文件格式的验证</strong>：验证 .class 文件字节流是否符合 class 文件的格式的规范，并且能够被当前版本的虚拟机处理。这里面主要对<strong>魔数、主版本号、常量池</strong>等等的校验（魔数、主版本号都是 .class 文件里面包含的数据信息、在这里可以不用理解）。</li><li><strong>元数据验证</strong>：主要是对字节码描述的信息进行语义分析，以保证其描述的信息符合 java 语言规范的要求，比如说验证这个类是不是有父类，类中的字段方法是不是和父类冲突等等。</li><li><strong>字节码验证</strong>：这是整个验证过程最复杂的阶段，主要是通过数据流和控制流分析，确定程序语义是合法的、符合逻辑的。在元数据验证阶段对数据类型做出验证后，这个阶段主要对类的方法做出分析，保证类的方法在运行时不会做出威海虚拟机安全的事。</li><li><strong>符号引用验证</strong>：它是验证的最后一个阶段，发生在虚拟机将符号引用转化为直接引用的时候。主要是对类自身以外的信息进行校验。目的是确保解析动作能够完成。</li></ol><p>对整个类加载机制而言，验证阶段是一个很重要但是非必需的阶段，如果我们的代码能够确保没有问题，那么我们就没有必要去验证，毕竟验证需要花费一定的的时间。当然我们可以使用 -Xverfity:none 来关闭大部分的验证。</p><h3 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h3><p>当完成字节码文件的校验之后，JVM 便会开始为类变量分配内存并初始化。这里需要注意两个关键点，即内存分配的对象以及初始化的类型。</p><ul><li><strong>内存分配的对象。</strong>Java 中的变量有「类变量」和「类成员变量」两种类型，「类变量」指的是被 static 修饰的变量，而其他所有类型的变量都属于「类成员变量」。在准备阶段，JVM 只会为「类变量」分配内存，而不会为「类成员变量」分配内存。「类成员变量」的内存分配需要等到初始化阶段才开始。</li></ul><p>例如下面的代码在准备阶段，只会为 factor 属性分配内存，而不会为 website 属性分配内存。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">factor</span> <span class="operator">=</span> <span class="number">3</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="type">String</span> <span class="variable">website</span> <span class="operator">=</span> <span class="string">&quot;adewu.github.io&quot;</span>;</span><br></pre></td></tr></table></figure><ul><li><strong>初始化的类型。</strong>在准备阶段，JVM 会为类变量分配内存，并为其初始化。但是这里的初始化指的是为<strong>变量赋予 Java 语言中该数据类型的零值</strong>，而不是用户代码里初始化的值。</li></ul><p>例如下面的代码在准备阶段之后，sector 的值将是 0，而不是 3。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">sector</span> <span class="operator">=</span> <span class="number">3</span>;</span><br></pre></td></tr></table></figure><p>但如果一个变量是常量<strong>（被 static final 修饰）的话，那么在准备阶段，属性便会被赋予用户希望的值</strong>。例如下面的代码在准备阶段之后，number 的值将是 3，而不是 0。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">number</span> <span class="operator">=</span> <span class="number">3</span>;</span><br></pre></td></tr></table></figure><p>之所以 static final 会直接被复制，而 static 变量会被赋予零值。其实我们稍微思考一下就能想明白了。</p><p>两个语句的区别是一个有 final 关键字修饰，另外一个没有。而 final 关键字在 Java 中代表不可改变的意思，意思就是说 number 的值一旦赋值就不会在改变了。既然一旦赋值就不会再改变，那么就必须一开始就给其赋予用户想要的值，因此被 final 修饰的类变量在准备阶段就会被赋予想要的值。而没有被 final 修饰的类变量，其可能在初始化阶段或者运行阶段发生变化，所以就没有必要在准备阶段对它赋予用户想要的值。 </p><h3 id="解析"><a href="#解析" class="headerlink" title="解析"></a>解析</h3><p>解析阶段是虚拟机常量池内的<strong>符号引用</strong>替换为<strong>直接引用</strong>的过程。</p><p><strong>符号引用</strong>：符号引用是一组符号来描述所引用的目标对象，符号可以是任何形式的字面量，只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关，引用的目标对象并不一定已经加载到内存中。Java 虚拟机明确在 Class 文件格式中定义的符号引用的字面量形式。</p><p><strong>直接引用</strong>：直接引用可以是直接指向目标对象的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机内存布局实现相关的，同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同，如果有了直接引用，那引用的目标必定已经在内存中存在。</p><p>在解析的阶段，解析动作主要针对7类符号引用进行，它们的名称以及对于常量池中的常量类型和解析报错信息如下：</p><p>| 解析动作 | 符号引用 | 解析可能的报错 | | ———- | ——————————- | ———————————————————–</p><p>| | 类或接口 | CONSTANTClassInfo | java.land.IllegalAccessError</p><p>| | 字段 | CONSTANTFieldrefInfo | java.land.IllegalAccessError 或 java.land.NoSuchFieldError</p><p>| | 类方法 | CONSTANTMethodefInfo | java.land.IllegalAccessError 或 java.land.NoSuchMethodError</p><p>| | 接口方法 | CONSTANTInterfaceMethoderInfo | java.land.IllegalAccessError 或 java.land.NoSuchMethodError</p><p>| | 方法类型 | CONSTANTMethodTypeInfo |</p><p>| | 方法句柄 | CONSTANTMethodhandlerInfo |</p><p>| | 调用限定符 | CONSTANTInvokeDynamicInfo | </p><p>解析的整个阶段在虚拟机中还是比较复杂的，远比上面介绍的复杂的多，但是很多特别细节的东西我们可以暂时先忽略，先有个大概的认识和了解之后有时间在慢慢深入了。</p><h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><p>类初始阶段是类加载过程的最后一步，在上面提到的类加载过程中，除了加载阶段用户应用程序可以通过自定义类加载器参与之外，其余的动作全部由虚拟机主导和控制。初始化阶段，是真正开始执行类中定义的 Java 程序代码（或者说是字节码）。</p><p>在准备阶段，变量已经赋值过一次系统要求的初始值（零值），而在初始化阶段，则根据程序员通过程序制定的主观计划去初始化类变量和其他资源。（或者从另一个角度表达：初始化阶段是执行类构造器 <code>&lt;clinit&gt;() </code>方法的过程。）</p><p>在这个阶段，JVM 会根据语句执行顺序对类对象进行初始化，一般来说当 JVM 遇到下面 5 种情况的时候会触发初始化：</p><ol><li>遇到 <strong>new、getstatic、putstatic、invokestatic</strong> 这四条字节码指令时，如果类没有进行过初始化，则需要先触发其初始化。生成这4条指令的最常见的 Java 代码场景是：使用new 关键字实例化对象的时候、读取或设置一个类的静态字段（被 final 修饰、已在编译器把结果放入常量池的静态字段除外）的时候，以及调用一个类的静态方法的时候。</li><li>使用 java.lang.reflect 包的方法对类进行<strong>反射调用</strong>的时候，如果类没有进行过初始化，则需要先触发其初始化。</li><li>当初始化一个类的时候，如果发现其父类还没有进行过初始化，则需要<strong>先触发其父类的初始化</strong>。</li><li>当虚拟机启动时，用户需要指定一个要<strong>执行的主类（包含 main() 方法的那个类）</strong>，虚拟机会先初始化这个主类。</li><li>当使用 JDK1.7 动态语言支持时，如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果 REF_getstatic，REF_putstatic，REF_invokeStatic 的方法句柄，并且这个方法句柄所对应的类没有进行初始化，则需要先出触发其初始化。</li></ol><p>看到上面几个条件你可能会晕了，但是不要紧，不需要背，知道一下就好，后面用到的时候回到找一下就可以了。 </p><p>注意这里的初始化，并不是说创造的类的实例，而是执行了类构造器，简单来说就是只对静态变量，静态代码块进行初始化。对于构造函数只有在创建实例的时候才会执行。</p><h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><p>当 JVM 完成初始化阶段之后，JVM 便开始从入口方法开始执行用户的程序代码。这个阶段也只是了解一下就可以。</p><h3 id="卸载"><a href="#卸载" class="headerlink" title="卸载"></a>卸载</h3><p>当用户程序代码执行完毕后，JVM 便开始销毁创建的 Class 对象，最后负责运行的 JVM 也退出内存。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Java 虚拟机的启动是通过引导类加载器 ( Bootstrap Class Loader ) 创建一个初始类 (Initial Class ) 来完成，这个类是由虚拟机的具体实现指定。紧接着，Java虚拟机链接这个初始类，初始化并调用它的 public void main</summary>
      
    
    
    
    <category term="Java" scheme="https://adewu.github.io/categories/Java/"/>
    
    
    <category term="Java类加载" scheme="https://adewu.github.io/tags/Java%E7%B1%BB%E5%8A%A0%E8%BD%BD/"/>
    
  </entry>
  
</feed>
