Android 12 - WMS 层级结构 && DisplayAreaGroup 引入

<h1 id="1-简介"><a href="#1-简介" class="headerlink" title="1. 简介"></a>1. 简介</h1><p>在 Android 窗口管理中,所有的窗口都是以树形数据结构进行组织管理的,认知这棵 WMS 的树有助于我们理解窗口的管理和显示,同时,WMS 的层级也决定了其在 <code>SurfaceFlinger</code> 的层级结构,这恰恰决定了它的显示规则。</p> <h1 id="2-WMS-顶层层级构建"><a href="#2-WMS-顶层层级构建" class="headerlink" title="2. WMS 顶层层级构建"></a>2. WMS 顶层层级构建</h1><p>在 Android 12 中,所有窗口树形管理都继基于 <code>WindowContainer,</code> 每个 <code>WindowContainer</code> 都有一个父节点和若干个子节点,我们先看看框架中 <code>WindowContainer</code> 都有哪些类型:</p> <p><img src="http://image.hanschen.site/master/2021-11-01-10-50-30.png" alt="2021-11-01-10-50-30"></p> <ul> <li>RootWindowContainer: 最顶层的管理者,直接管理 DisplayContent</li> <li>DisplayContent: 代表着一个真实或者虚拟的显示设备,在普遍场景中,系统中只存在一个 DisplayContent</li> <li>TaskDisplayArea: 是系统中所有应用任务的父节点,用于管理 Task</li> <li>Task: 代表着一个任务</li> <li>ActivityRecord: 代表一个 Activity 节点</li> <li>WallpaperWindowToken: 代表壁纸节点</li> <li>…</li> </ul>

2021/10/25
阅读更多

Android 12 - 跟踪利器 WinScope

<h1 id="1-简介"><a href="#1-简介" class="headerlink" title="1. 简介"></a>1. 简介</h1><p>在开发过程中,经常会遇到各种各样的窗口问题,比如动画异常、窗口异常、闪屏、黑屏、错位显示..</p> <p>以前对于这些问题,我们可以通过添加日志,调试分析代码等手段去解决,但这些 UI 问题往往出现在一瞬间,很难把握出现的时机,录制下来的日志往往也是巨大的,从海量的日志中提取有效的信息是一个枯燥且繁琐的事情。</p> <p>Android 也意识到了这个问题,<code>WinScope</code> 的出现有效的帮助我们跟踪窗口和显示问题。它向开发者提供一个可视化的工具,让开发者能使用工具跟踪整个界面的变化过程,让我们可以观察到细微的变化。迭代了几个版本后,Android 12 上 WinScope 变得更好用了,下面来看看大概的效果:</p> <p><img src="http://image.hanschen.site/master/2021-10-22-14-26-57.png" alt="2021-10-22-14-26-57"></p>

2021/10/21
阅读更多

Android 12 - Letterbox 模式

<h1 id="1-简介"><a href="#1-简介" class="headerlink" title="1. 简介"></a>1. 简介</h1><p>随着越来越多大屏和折叠屏设备出现,很多应用并未对不同尺寸的设备进行 UI 适配,这时候应用选择以特定的宽高比显示(<strong>虽然 Google 不建议这这样做,官方还是希望开发者可以对不同的屏幕尺寸进行自适应布局~</strong>),当应用的宽高比和它的容器比例不兼容的时候,就会以 Letterbox 模式打开。</p> <p><img src="http://image.hanschen.site/master/2021-10-21-18-17-07.png" alt="2021-10-21-18-17-07"></p> <p>Letterbox 模式下界面会以指定的比例显示,周围空白区域可以填充壁纸或者颜色。至于 Letterbox 的外观可受以下因素影响:</p> <ul> <li><code>config_letterboxActivityCornersRadius</code>: 界面圆角大小</li> <li><code>config_letterboxBackgroundType</code>: 背景填充类型,分别有: <ul> <li><code>LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND</code>: 颜色受 <code>android:colorBackground</code> 影响</li> <li><code>LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING</code>: 颜色受 <code>android:colorBackgroundFloating</code> 影响</li> <li><code>LETTERBOX_BACKGROUND_SOLID_COLOR</code>: 颜色受 <code>config_letterboxBackgroundColor</code> 影响</li> <li><code>LETTERBOX_BACKGROUND_WALLPAPER</code>: 显示壁纸,此选项和 <code>FLAG_SHOW_WALLPAPER</code> 类似,会导致壁纸窗口显示</li> </ul> </li> <li><code>config_letterboxBackgroundWallpaperBlurRadius</code>: 壁纸模糊程度</li> <li><code>config_letterboxBackgroundWallaperDarkScrimAlpha</code>: 壁纸变暗程度</li> </ul>

2021/10/20
阅读更多

Android 列表滚动优化之 OverScroller 揭秘

<h1 id="1-简介"><a href="#1-简介" class="headerlink" title="1. 简介"></a>1. 简介</h1><p><code>OverScroller</code> 在 Android 系统中承担着为 ListView、RecyclerView、ScrollView 这些滚动控件计算实时滑动位置的任务,这些位置算法直接影响着每一次滚动的体验</p> <p>众所周知,Android 的动画体验<del>远</del>不如 iOS,即便如今 Android 已普遍支持 120Hz 高刷,体验起来也不是非常舒服。究其原因已经不是硬件性能限制,而是其中很多动画设计本身就有问题。苹果早在很早之前就发布了 <a href="https://developer.apple.com/videos/play/wwdc2018/803" target="_blank" rel="noopener">Designing Fluid Interfaces</a> 致力于打造一个丝滑流畅的用户体验,反观 Android,对于一个日常使用中使用最多的滑动工具类 <code>OverScroller</code> 近几年改进竟然寥寥无几,几乎没有,实在是有点想吐槽</p> <p>这个系列分为两篇,第一篇主要讲述 Android 实现滚动的核心工具类 <code>OverScroller</code> 的使用方法和原理,第二篇我们将探索如何进行改进,希望我们每一次探索都能给用户体验带来提升</p>

2021/4/8
阅读更多

使用 Ninja 提升模块编译速度

<h1 id="1-简介"><a href="#1-简介" class="headerlink" title="1. 简介"></a>1. 简介</h1><p>从 Android 7 开始,Android 源码编译时默认使用 Ninja,编译时,会先把 makefile 和 bp 转换成 ninja 再进行编译。这个转换过程非常慢(需要遍历处理所有关联的 makefile、bp 文件),即使只是通过 <code>mm</code> 或 <code>mmm</code> 编译某个模块,也会有很多因素触发 ninja 文件的重新生成,而这对基于源码开发的模块很不友好,编译好慢!</p>

2019/11/19
阅读更多

Android Q 黑暗模式(Dark Mode)源码解析

<h1 id="1-简介"><a href="#1-简介" class="headerlink" title="1. 简介"></a>1. 简介</h1><p>随着 Android Q 发布,「黑暗模式」或者说是「夜间模式」终于在此版本中得到了支持,官方介绍见:<a href="https://developer.android.com/guide/topics/ui/look-and-feel/darktheme" target="_blank" rel="noopener">https://developer.android.com/guide/topics/ui/look-and-feel/darktheme</a>,再看看效果图:</p> <p><img src="http://image.hanschen.site/master/2019-10-21-17-21-50.png" alt="2019-10-21-17-21-50.png"></p> <p>其实这个功能魅族在两年前就已支持,不得不说 Android 有点落后了,今天我们就来看看原生是怎么实现全局夜间模的吧</p>

2019/10/21
阅读更多

如何顺滑地查看 Android Native 代码

<h1 id="1-简介"><a href="#1-简介" class="headerlink" title="1. 简介"></a>1. 简介</h1><p>使用 Android Studio 查看 Android Framework 代码体验非常好,无论是索引还是界面都让人很满意,但是当你跟踪代码,发现进入 native 逻辑时,就会发现 Android Studio 对 native 代码的支持非常不好,不能索引不支持符号搜索不能跳转等,这些让人非常抓狂。那么如何能在 IDE 愉快地查看 native 代码呢?在 Windows 上,Source Insight 的表现也很好,但苦于只有 Windows 平台支持且界面不好,经过一番折腾,还真是找到了方法,下面我们将一步一步打造丝滑的 native 代码阅读环境。</p>

2019/10/10
阅读更多

AOSP 编译和烧写

<h1 id="1-简介"><a href="#1-简介" class="headerlink" title="1. 简介"></a>1. 简介</h1><p>很多 Android 开发者都会希望编译 Android 源码并刷进自己的手机里面,但网上教程很多都仅仅是告诉你 lunch、make 等等,但你手里有一台设备时却发现,你编译出的镜像由于驱动关系是不能直接烧进手机的。这里整理了一下步骤,帮助大家可以按照流程编译并烧写镜像。</p>

2019/9/11
阅读更多

Protocol Buffers 手册

<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>Protocol Buffers 是 google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。</p> <p>至于protobuf是什么、使用场景、有什么好处,本文不做说明,这里将会为大家介绍怎么用 <code>protobuf</code> 来定义我们的交互协议,包括 <code>.proto</code> 的语法以及如何根据proto文件生成相应的代码。本文基于<a href="https://developers.google.com/protocol-buffers/docs/proto3" target="_blank" rel="noopener">proto3</a>,读者也可以点击了解<a href="https://developers.google.com/protocol-buffers/docs/proto" target="_blank" rel="noopener">proto2</a></p>

2017/4/7
阅读更多

设计模式之装饰模式

<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>装饰模式(Decorator)也叫包装器模式(Wrapper),是指动态地给一个对象添加一些额外的职责,就增加功能来说装饰模式比生成子类更为灵活。它通过创建一个包装对象,也就是装饰来包裹真实的对象</p> <h1 id="情景举例"><a href="#情景举例" class="headerlink" title="情景举例"></a>情景举例</h1><p>我们先来分析这样一个画图形的需求:</p> <ol> <li>它能绘制各种背景,如红色、蓝色、绿色</li> <li>它能绘制形状,如三角形,正方形,圆形</li> <li>它能给形状加上阴影</li> </ol> <p>就先列这三个简单的需求吧,下面让我们比较下各种实现的优缺点</p> <h1 id="丑陋的实现"><a href="#丑陋的实现" class="headerlink" title="丑陋的实现"></a>丑陋的实现</h1><p>来看看我们用继承是如何实现的,首先,抽象出一个<code>Shape</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> HansChen</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Shape</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 绘制图形</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">draw</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>然后我们定义各种情况下的子类,结构如下,看到这么多的子类,是不是有点要爆炸的感觉?真是想想都可怕<br><img src="http://image.hanschen.site/master/2019-9-2-12-35-9.png" alt="2019-9-2-12-35-9.png"></p> <p>而且如果再新增一种需求,比如现在要画椭圆,那么维护的人员估计就要爆粗了吧?</p>

2017/3/8
阅读更多

设计模式之桥接模式

<h1 id="场景问题"><a href="#场景问题" class="headerlink" title="场景问题"></a>场景问题</h1><h2 id="发送消息"><a href="#发送消息" class="headerlink" title="发送消息"></a>发送消息</h2><p>现在我们要实现这样一个功能:发送消息。从业务上看,消息又分成普通消息、加急消息和特急消息多种,不同的消息类型,业务功能处理是不一样的,比如加急消息是在消息上添加“加急”字样,而特急消息除了添加特急外,还会做一条催促的记录,多久不完成会继续催促。从发送消息的手段上看,又有系统内短消息、手机短消息、邮件等等。现在要实现这样的发送提示消息的功能,该如何实现呢?</p>

2017/2/28
阅读更多

设计模式之代理模式

<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>我们执行一个功能的函数时,经常需要在其中写入与功能不是直接相关但很有必要的代码,如日志记录、信息发送、安全和事务支持等,这些<code>枝节性代码</code>虽然是必要的,但它会带来以下麻烦:</p> <ul> <li>枝节性代码游离在功能性代码之外,它下是函数的目的</li> <li>枝节性代码会造成功能性代码对其它类的依赖,加深类之间的耦合</li> <li>枝节性代码带来的耦合度会造成功能性代码移植困难,可重用性降低</li> </ul> <p>毫无疑问,枝节性代码和功能性代码需要分开来才能降低耦合程度,我们可以使用<strong>代理模式(委托模式)</strong>完成这个要求。代理模式的作用是:为其它对象提供一种代理以控制对这个对象的访问。在某些情况下,一 个客户不想直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。</p> <p>代理模式一般涉及到三个角色:</p> <ul> <li><strong>抽象角色</strong>:声明真实对象和代理对象的共同接口</li> <li><strong>代理角色</strong>:代理对象内部包含有真实角色的引用,从而可以操作真实角色,同时代理对象 与真实对象有相同的接口,能在任何时候代替真实对象,代理对象可以在执行真实对 象前后加入特定的逻辑以实现功能的扩展。</li> <li><strong>真实角色</strong>:代理角色所代表的真实对象,是我们最终要引用的对象</li> </ul> <p>常见的代理应用场景有:</p> <ul> <li><strong>远程代理</strong>:对一个位于不同的地址空间对象提供一个局域代表对象,如RMI中的stub</li> <li><strong>虚拟代理</strong>:根据需要将一个资源消耗很大或者比较复杂的对象,延迟加载,在真正需要的时候才创建</li> <li><strong>保护代理</strong>:控制对一个对象的访问权限</li> <li><strong>智能引用</strong>:提供比目标对象额外的服务和功能</li> </ul> <p>接下来,我们用代码来说明什么是代理模式</p>

2016/12/26
阅读更多

依赖注入利器 - Dagger ‡

<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>在开发过程中,为了实现解耦,我们经常使用依赖注入,常见的依赖注入方式有:</p> <ul> <li><strong>构造方法注入</strong>:在构造方法中把依赖作为参数传递进去</li> <li><strong>setter方法注入</strong>:添加setter方法,把依赖传递进去</li> <li><strong>接口注入</strong>:把注入方法抽到一个接口中,然后实现该接口,把依赖传递进去</li> </ul> <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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PersonService</span> <span class="keyword">implements</span> <span class="title">DependencyInjecter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> PersonDao personDao;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 构造方法注入</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">PersonService</span><span class="params">(PersonDao personDao)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">this</span>.personDao = personDao;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// setter方法注入</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setPersonDao</span><span class="params">(PersonDao personDao)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">this</span>.personDao = personDao;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 接口注入:实现DependencyInjecter接口</span></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">injectPersonDao</span><span class="params">(PersonDao personDao)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">this</span>.personDao = personDao;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> ... ... </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

2016/12/17
阅读更多

Robolectric使用教程

<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>Android的单元测试可以分为两部分:</p> <ol> <li>Local unit tests:运行于本地JVM</li> <li>Instrumented test:运行于真机或者模拟器</li> </ol> <p>如果使用Local测试,需要保证测试过程中不会调用Android系统API,否则会抛出RuntimeException异常,因为Local测试是直接跑在本机JVM的,而之所以我们能使用Android系统API,是因为编译的时候,我们依赖了一个名为“android.jar”的jar包,但是jar包里所有方法都是直接抛出了一个RuntimeException,是没有任何任何实现的,这只是Android为了我们能通过编译提供的一个Stub!当APP运行在真实的Android系统的时候,由于类加载机制,会加载位于framework的具有真正实现的类。由于我们的Local是直接在PC上运行的,所以调用这些系统API便会出错。<br>那么问题来了,我们既要使用Local测试,但测试过程又难免遇到调用系统API那怎么办?其中一个方法就是mock objects,比如借助Mockito,另外一种方式就是使用<code>Robolectric</code>, Robolectric就是为解决这个问题而生的。它实现一套JVM能运行的Android代码,然后在unit test运行的时候去截取android相关的代码调用,然后转到他们的他们实现的Shadow代码去执行这个调用的过程</p>

2016/12/9
阅读更多

设计模式之模板方法模式和策略模式

<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>我们知道,OOP三个基本特征是:封装、继承、多态。通过继承,我们可以基于差异编程,也就是说,对于一个满足我们大部分需求的类,可以创建它的一个子类并只改变我们不期望的那部分。但是在实际使用中,继承很容易被过度使用,并且过度使用的代价是比较高的,所以我们减少了继承的使用,使用组合或委托代替</p> <blockquote> <p>优先使用对象组合而不是类继承</p> </blockquote> <p>在本文中,我们会分别介绍<code>模板方法模式</code>和<code>策略模式</code>,这两个模式分别使用了继承和委托两种方式。这两种模式解决的问题是类似的,经常可以互换使用,它们都可以分离通用的算法和具体的上下文。比如我们有一个通用的算法,算法有不同的实现方式,为了遵循依赖倒置原则,我们希望算法不依赖于具体实现。</p>

2016/11/30
阅读更多

设计模式之工厂模式(Factory)

<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>根据<code>依赖倒置原则</code>,我们知道,我们应优先依赖抽象类而不是具体类。在应用开发过程中,有很多实体类都是非常易变的,依赖它们会带来问题,所以我们更应该依赖于抽象接口,已使我们免受大多数变化的影响。<br><code>工厂模式(Factory)</code>允许我们只依赖于抽象接口就能创建出具体对象的实例,所以在开发中,如果具体类是高度易变的,那么该模式就非常有用。</p> <p>接下来我们就通过代码举例说明什么是工厂模式</p>

2016/11/25
阅读更多

Fragment源码分析

<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p><code>Fragment</code>表示 Activity 中的行为或用户界面部分。您可以将多个 Fragment 组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个 Fragment。您可以将 Fragment 视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除 Fragment。<br>Fragment 必须始终嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。 例如,当 Activity 暂停时,其中的所有 Fragment 也会暂停;当 Activity 被销毁时,所有 Fragment 也会被销毁。 不过,当 Activity 正在运行(处于已恢复生命周期状态)时,您可以独立操纵每个 Fragment,如添加或移除它们。 当您执行此类 Fragment 事务时,您也可以将其添加到由 Activity 管理的返回栈 — Activity 中的每个返回栈条目都是一条已发生 Fragment 事务的记录。 返回栈让用户可以通过按返回按钮撤消 Fragment 事务(后退)。</p> <p>当您将 Fragment 作为 Activity 布局的一部分添加时,它存在于 Activity 视图层次结构的某个 ViewGroup 内部,并且 Fragment 会定义其自己的视图布局。您可以通过在 Activity 的布局文件中声明Fragment,将其作为 <code>&lt;fragment&gt;</code> 元素插入您的 Activity 布局中,或者通过将其添加到某个现有 ViewGroup,利用应用代码进行插入。不过,Fragment 并非必须成为 Activity 布局的一部分;您还可以将没有自己 UI 的 Fragment 用作 Activity 的不可见工作线程。</p> <p>本文将通过分析源码,对 Fragment 的创建、销毁以及生命周期做一个更深入的认识。</p> <p>建议读者在看这篇文章的时候,先看下<a href="http://blog.csdn.net/shensky711/article/details/53132952" target="_blank" rel="noopener">Fragment事务管理源码分析</a>,对Fragment管理类先有一个比较清楚的认识。</p>

2016/11/14
阅读更多

Fragment事务管理源码分析

<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>在Fragment使用中,有时候需要对Fragment进行<code>add</code>、<code>remove</code>、<code>show</code>、<code>hide</code>、<code>replace</code>等操作来进行Fragment的显示隐藏等管理,这些管理是通过<code>FragmentTransaction</code>进行事务管理的。事务管理是对于一系列操作进行管理,一个事务包含一个或多个操作命令,是逻辑管理的工作单元。一个事务开始于第一次执行操作语句,结束于Commit。通俗地将,就是把多个操作缓存起来,等调用commit的时候,统一批处理。下面会对Fragmeng的事务管理做一个代码分析</p>

2016/11/10
阅读更多

java动态代理

<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>动态代理是java的一大特性,动态代理的优势就是实现无侵入式的代码扩展。它可以增强我们原有的方法,比如常用的日志监控,添加缓存等,也可以实现方法拦截,通过代理方法修改原方法的参数和返回值等。<br>要了解动态代理,我们需要先看看什么是静态代理</p>

2016/10/19
阅读更多

Android分包MultiDex源码分析

<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>Android开发者应该都遇到了64K最大方法数限制的问题,针对这个问题,google也推出了multidex分包机制,在生成apk的时候,把整个应用拆成n个dex包(classes.dex、classes2.dex、classes3.dex),每个dex不超过64k个方法。使用multidex,在5.0以前的系统,应用安装时只安装main dex(包含了应用启动需要的必要class),在应用启动之后,需在Application的<code>attachBaseContext</code>中调用<code>MultiDex.install(base)</code>方法,在这时候才加载第二、第三…个dex文件,从而规避了64k问题。<br>当然,在<code>attachBaseContext</code>方法中直接install启动second dex会有一些问题,比如install方法是一个同步方法,当在主线程中加载的dex太大的时候,耗时会比较长,可能会触发ANR。不过这是另外一个问题了,解决方法可以参考:<a href="http://blog.csdn.net/shensky711/article/details/52329035" target="_blank" rel="noopener">Android最大方法数和解决方案</a> <a href="http://blog.csdn.net/shensky711/article/details/52329035。" target="_blank" rel="noopener">http://blog.csdn.net/shensky711/article/details/52329035。</a></p> <p>本文主要分析的是<code>MultiDex.install()</code>到底做了什么,如何把secondary dexes中的类动态加载进来。</p>

2016/10/17
阅读更多