目前来说,对于使用Android Studio的朋友来说,MultiDex应该不陌生,就是Google为了解决『65535天花板』问题而给出的官方解决方案,但是这个方案并不完美,所以美团又给出了异步加载Dex文件的方案。今天这篇文章是我最近研究MultiDex方案的一点收获,最后还留了一个没有解决的问题,如果你有思路的话,欢迎交流!
单个Dex文件中,method个数采用使用原生类型short来索引,即4个字节最多65536个method,field、class的个数也均有此限制,关于如何解决由于引用过多基础依赖项目,造成field超过65535问题,请参考@寒江不钓的这篇文章『当Field邂逅65535』。
对于Dex文件,则是将工程所需全部class文件合并且压缩到一个DEX文件期间,也就是使用Dex工具将class文件转化为Dex文件的过程中, 单个Dex文件可被引用的方法总数(自己开发的代码以及所引用的Android框架、类库的代码)被限制为65536。
这就是65535问题的根本来源。
这个问题多发生在2.x版本的设备上,安装时会提示INSTALL_FAILED_DEXOPT,这个问题发生在安装期间,在使用Dalvik虚拟机的设备上安装APK时,会通过DexOpt工具将Dex文件优化为ODex文件,即Optimised Dex,这样可以提高执行效率。
在Android版本不同分别经历了4M/5M/8M/16M限制,目前主流4.2.x系统上可能都已到16M, 在Gingerbread或以下系统LinearAllocHdr分配空间只有5M大小的, 高于Gingerbread的系统提升到了8M。Dalvik linearAlloc是一个固定大小的缓冲区。dexopt使用LinearAlloc来存储应用的方法信息。Android 2.2和2.3的缓冲区只有5MB,Android 4.x提高到了8MB或16MB。当应用的方法信息过多导致超出缓冲区大小时,会造成dexopt崩溃,造成INSTALL_FAILED_DEXOPT错误。
当App不断迭代的时候,总有一天会遇到这个问题,为此Google也给出了解决方案,具体的操作步骤我就不多说了,无非就是配置Application和Gradle文件,下面我们简单看一下这个方案的实现原理。
实际起作用的是下面这个jar包
~/sdk/extras/android/support/multidex/library/libs/android-support-multidex.jar
不管是继承自MultiDexApplication还是重写attachBaseContext(),实际都是调用下面的方法
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MultiDexApplication</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Application</span> {</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">attachBaseContext</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Context base) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.attachBaseContext(base); MultiDex.install((Context)<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>
下面重点看下MutiDex.install(Context)的实现,代码很容易理解,重点的地方都有注释
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//第二个Dex文件的文件夹名,实际地址是/date/date/<package_name>/code_cache/secondary-dexes</span> SECONDARY_FOLDER_NAME = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"code_cache"</span> + File.separator + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"secondary-dexes"</span>; installedApk = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> HashSet<String>(); IS_VM_MULTIDEX_CAPABLE = isVMMultidexCapable(System.getProperty(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"java.vm.version"</span>)); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">install</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Context context) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//在使用ART虚拟机的设备上(部分4.4设备,5.0+以上都默认ART环境),已经原生支持多Dex,因此就不需要手动支持了</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (MultiDex.IS_VM_MULTIDEX_CAPABLE) { Log.i(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MultiDex"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"VM has multidex support, MultiDex support library is disabled."</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (Build.VERSION.SDK_INT < <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Multi dex installation failed. SDK "</span> + Build.VERSION.SDK_INT + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" is unsupported. Min SDK version is "</span> + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span> + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"."</span>); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> ApplicationInfo applicationInfo = getApplicationInfo(context); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (applicationInfo == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span> (MultiDex.installedApk) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//如果apk文件已经被加载过了,就返回</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String apkPath = applicationInfo.sourceDir; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (MultiDex.installedApk.contains(apkPath)) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>; } MultiDex.installedApk.add(apkPath); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (Build.VERSION.SDK_INT > <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">20</span>) { Log.w(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MultiDex"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MultiDex is not guaranteed to work in SDK version "</span> + Build.VERSION.SDK_INT + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">": SDK version higher than "</span> + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">20</span> + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" should be backed by "</span> + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"runtime with built-in multidex capabilty but it's not the "</span> + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"case here: java.vm.version=\""</span> + System.getProperty(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"java.vm.version"</span>) + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\""</span>); } ClassLoader loader; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> { loader = context.getClassLoader(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (RuntimeException e) { Log.w(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MultiDex"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Failure while trying to obtain Context class loader. Must be running in test mode. Skip patching."</span>, (Throwable)e); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (loader == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { Log.e(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MultiDex"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Context class loader is null. Must be running in test mode. Skip patching."</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//清楚之前的Dex文件夹,之前的Dex放置在这个文件夹</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//final File dexDir = new File(context.getFilesDir(), "secondary-dexes");</span> clearOldDexDir(context); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Throwable t) { Log.w(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MultiDex"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Something went wrong when trying to clear old MultiDex extraction, continuing without cleaning."</span>, t); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> File dexDir = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(applicationInfo.dataDir, MultiDex.SECONDARY_FOLDER_NAME); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//将Dex文件加载为File对象</span> List<File> files = MultiDexExtractor.load(context, applicationInfo, dexDir, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//检测是否是zip文件</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (checkValidZipFiles(files)) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//正式安装其他Dex文件</span> installSecondaryDexes(loader, dexDir, files); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { Log.w(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MultiDex"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Files were not valid zip files. Forcing a reload."</span>); files = MultiDexExtractor.load(context, applicationInfo, dexDir, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!checkValidZipFiles(files)) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Zip files were not valid."</span>); } installSecondaryDexes(loader, dexDir, files); } } } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e2) { Log.e(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MultiDex"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Multidex installation failure"</span>, (Throwable)e2); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Multi dex installation failed ("</span> + e2.getMessage() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">")."</span>); } Log.i(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MultiDex"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"install done"</span>); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li></ul>
从上面的过程来看,只是完成了加载包含着Dex文件的zip文件,具体的加载操作都在下面的方法中
<code class="hljs scss has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-function" style="box-sizing: border-box;">installSecondaryDexes(loader, dexDir, files)</span>;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
下面重点看下
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">installSecondaryDexes</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> ClassLoader loader, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> File dexDir, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> List<File> files) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IOException { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!files.isEmpty()) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (Build.VERSION.SDK_INT >= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">19</span>) { install(loader, files, dexDir); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (Build.VERSION.SDK_INT >= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">14</span>) { install(loader, files, dexDir); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { install(loader, files); } } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>
到这里为了完成不同版本的兼容,实际调用了不同类的方法,我们仅看一下>=14的版本,其他的类似
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">V14</span> {</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">install</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> ClassLoader loader, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> List<File> additionalClassPathEntries, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> File optimizedDirectory) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//通过反射获取loader的pathList字段,loader是由Application.getClassLoader()获取的,实际获取到的是PathClassLoader对象的pathList字段</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Field pathListField = findField(loader, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"pathList"</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Object dexPathList = pathListField.get(loader); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//dexPathList是PathClassLoader的私有字段,里面保存的是Main Dex中的class</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//dexElements是一个数组,里面的每一个item就是一个Dex文件</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//makeDexElements()返回的是其他Dex文件中获取到的Elements[]对象,内部通过反射makeDexElements()获取</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//expandFieldArray是为了把makeDexElements()返回的Elements[]对象添加到dexPathList字段的成员变量dexElements中</span> expandFieldArray(dexPathList, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"dexElements"</span>, makeDexElements(dexPathList, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ArrayList<File>(additionalClassPathEntries), optimizedDirectory)); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> Object[] <span class="hljs-title" style="box-sizing: border-box;">makeDexElements</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Object dexPathList, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> ArrayList<File> files, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> File optimizedDirectory) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> IllegalAccessException, InvocationTargetException, NoSuchMethodException { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Method makeDexElements = findMethod(dexPathList, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"makeDexElements"</span>, (Class<?>[])<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Class[] { ArrayList.class, File.class }); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> (Object[])makeDexElements.invoke(dexPathList, files, optimizedDirectory); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li></ul>
PathClassLoader.java
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">PathClassLoader</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">BaseDexClassLoader</span> {</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">PathClassLoader</span>(String dexPath, ClassLoader parent) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>(dexPath, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>, parent); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">PathClassLoader</span>(String dexPath, String libraryPath, ClassLoader parent) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>(dexPath, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>, libraryPath, parent); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>
BaseDexClassLoader的代码如下,实际上寻找class时,会调用findClass(),会在pathList中寻找,因此通过反射手动添加其他Dex文件中的class到pathList字段中,就可以实现类的动态加载,这也是MutiDex方案的基本原理。
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">BaseDexClassLoader</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ClassLoader</span> {</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> DexPathList pathList; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">BaseDexClassLoader</span>(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>(parent); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.pathList = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DexPathList(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, dexPath, libraryPath, optimizedDirectory); } <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> Class<?> <span class="hljs-title" style="box-sizing: border-box;">findClass</span>(String name) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> ClassNotFoundException { List<Throwable> suppressedExceptions = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ArrayList<Throwable>(); Class c = pathList.findClass(name, suppressedExceptions); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (c == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { ClassNotFoundException cnfe = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ClassNotFoundException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Didn't find class \""</span> + name + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\" on path: "</span> + pathList); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (Throwable t : suppressedExceptions) { cnfe.addSuppressed(t); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> cnfe; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> c; } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li></ul>
通过查看MultiDex的源码,可以发现MultiDex在冷启动时,因为会同步的反射安装Dex文件,进行IO操作,容易导致ANR
首先我们要明白,美团的这个动态异步加载方案,和插件化的动态加载方案要解决的问题不一样,我们这里讨论的只是单纯的为了解决65535问题,并且想办法解决Google的MutiDex方案的弊端。
首先,采用Google的方案我们不需要关心Dex分包,开发工具会自动的分析依赖关系,把需要的class文件及其依赖class文件放在Main Dex中,因此如果产生了多个Dex文件,那么classes.dex内的方法数一般都接近65535这个极限,剩下的class才会被放到Other Dex中。如果我们可以减小Main Dex中的class数量,是可以加快冷启动速度的。
美团给出了Gradle的配置,但是由于没有具体的实现,所以这块还需要研究。
<code class="hljs rust has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;">tasks.whenTaskAdded { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span> -> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.name.startsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'proguard'</span>) && (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.name.endsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'Debug'</span>) || <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.name.endsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'Release'</span>))) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.doLast { makeDexFileAfterProguardJar(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.doFirst { delete <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${project.buildDir}/intermediates/classes-proguard"</span>; String flavor = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.name.substring(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'proguard'</span>.length(), <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.name.lastIndexOf(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.name.endsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'Debug'</span>) ? <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Debug"</span> : <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Release"</span>)); generateMainIndexKeepList(flavor.toLowerCase()); } } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.name.startsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'zipalign'</span>) && (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.name.endsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'Debug'</span>) || <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.name.endsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'Release'</span>))) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">task</span>.doFirst { ensureMultiDexInApk(); } } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>
实现Dex自定义分包的关键是分析出class之间的依赖关系,并且干涉Dex文件的生成过程。
Dex也是一个工具,通过设置参数可以实现哪一些class文件在Main Dex中。
<code class="hljs livecodeserver has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;">afterEvaluate { tasks.matching { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">it</span>.name.startsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'dex'</span>) }.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">each</span> { dx -> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (dx.additionalParameters == <span class="hljs-constant" style="box-sizing: border-box;">null</span>) { dx.additionalParameters = [] } dx.additionalParameters += <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'--multi-dex'</span> dx.additionalParameters += <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'--set-max-idx-number=30000'</span> println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"dx param = "</span>+dx.additionalParameters) dx.additionalParameters += <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"--main-dex-list=$projectDir/multidex.keep"</span>.toString() } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>
需要注意的是,上面我给出的gredle task,只在1.4以下管用,在1.4+版本的gradle中,app:dexXXX task 被隐藏了(更多信息请参考Gradle plugin的更新信息),jacoco, progard, multi-dex三个task被合并了。
<code class="hljs scala has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;">The Dex task is not available through the variant API anymore…. The goal of <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span> API is to simplify injecting custom <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">manipulations</span> without <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">having</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">to</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">deal</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">with</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">tasks</span>, <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">and</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">to</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">offer</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">more</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">flexibility</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">on</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">what</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">is</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">manipulated</span>. <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">The</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">internal</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">code</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">processing</span> <span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(jacoco, progard, multi-dex)</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">have</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">all</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">moved</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">to</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">this</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">new</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">mechanism</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">already</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">in</span> 1.5.0-<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">beta1</span>.</span> </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>
所以通过上面的方法无法对Dex过程进行劫持。这也是我现在还没有解决的问题,有解决方案的朋友可以指点一下!
其实前面的操作都是为了这一步操作的,无论将Dex分成什么样,如果不能异步加载,就解决不了ANR和加载白屏的问题,所以异步加载是一个重点。
异步加载主要问题就是:如何避免在其他Dex文件未加载完成时,造成的ClassNotFoundException问题?
美团给出的解决方案是替换Instrumentation,但是博客中未给出具体实现,我对这个技术点进行了简单的实现,Demo在这里MultiDexAsyncLoad,对ActivityThread的反射用的是携程的解决方案。
首先继承自Instrumentation,因为这一块需要涉及到Activity的启动过程,所以对这个过程不了解的朋友请看我的这篇文章【凯子哥带你学Framework】Activity启动过程全解析。
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * Created by zhaokaiqiang on 15/12/18. */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MeituanInstrumentation</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Instrumentation</span> {</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> List<String> mByPassActivityClassNameList; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">MeituanInstrumentation</span>() { mByPassActivityClassNameList = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ArrayList<>(); } <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Activity <span class="hljs-title" style="box-sizing: border-box;">newActivity</span>(ClassLoader cl, String className, Intent intent) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> InstantiationException, IllegalAccessException, ClassNotFoundException { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (intent.getComponent() != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { className = intent.getComponent().getClassName(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> shouldInterrupted = !MeituanApplication.isDexAvailable(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mByPassActivityClassNameList.contains(className)) { shouldInterrupted = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (shouldInterrupted) { className = WaitingActivity.class.getName(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { mByPassActivityClassNameList.add(className); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.newActivity(cl, className, intent); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li></ul>
至于为什么重写了newActivity(),是因为在启动Activity的时候,会经过这个方法,所以我们在这里可以进行劫持,如果其他Dex文件还未异步加载完,就跳转到Main Dex中的一个等待Activity——WaitingActivity。
<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"> private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.activityInfo</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> if (r<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.packageInfo</span> == null) { r<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.packageInfo</span> = getPackageInfo(aInfo<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.applicationInfo</span>, r<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.compatInfo</span>, Context<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.CONTEXT</span>_INCLUDE_CODE)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> } Activity activity = null<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> try { java<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.lang</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.ClassLoader</span> cl = r<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.packageInfo</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getClassLoader</span>()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> activity = mInstrumentation<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.newActivity</span>( cl, component<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getClassName</span>(), r<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.intent</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> } catch (Exception e) { } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>
在WaitingActivity中可以一直轮训,等待异步加载完成,然后跳转至目标Activity。
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">WaitingActivity</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">BaseActivity</span> {</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Timer timer; <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onCreate</span>(Bundle savedInstanceState) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onCreate(savedInstanceState); setContentView(R.layout.activity_wait); waitForDexAvailable(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">waitForDexAvailable</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Intent intent = getIntent(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String className = intent.getStringExtra(TAG_TARGET); timer = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Timer(); timer.schedule(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TimerTask() { <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (!MeituanApplication.isDexAvailable()) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> { Thread.sleep(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">100</span>); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (InterruptedException e) { e.printStackTrace(); } Log.d(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"TAG"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"waiting"</span>); } intent.setClassName(getPackageName(), className); startActivity(intent); finish(); } }, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>); } <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onDestroy</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onDestroy(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (timer != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { timer.cancel(); } } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li></ul>
异步加载Dex文件放在什么时候合适呢?
我放在了Application.onCreate()中
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MeituanApplication</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Application</span> {</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String TAG = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MeituanApplication"</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> isDexAvailable = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>; <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onCreate</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onCreate(); loadOtherDexFile(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">loadOtherDexFile</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Thread(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Runnable() { <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() { MultiDex.install(MeituanApplication.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>); isDexAvailable = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>; } }).start(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">isDexAvailable</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> isDexAvailable; } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>
那么替换系统默认的Instrumentation在什么时候呢?
当SplashActivity跳转到MainActivity之后,再进行替换比较合适,于是
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MainActivity</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">BaseActivity</span> {</span> <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onCreate</span>(Bundle savedInstanceState) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MeituanApplication.attachInstrumentation(); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>
MeituanApplication.attachInstrumentation()实际就是通过反射替换默认的Instrumentation。
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MeituanApplication</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Application</span> {</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">attachInstrumentation</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> { SysHacks.defineAndVerify(); MeituanInstrumentation meiTuanInstrumentation = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> MeituanInstrumentation(); Object activityThread = AndroidHack.getActivityThread(); Field mInstrumentation = activityThread.getClass().getDeclaredField(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"mInstrumentation"</span>); mInstrumentation.setAccessible(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>); mInstrumentation.set(activityThread, meiTuanInstrumentation); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) { e.printStackTrace(); } } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li></ul>
至此,异步加载Dex方案的一个基本思路就通了,剩下的就是完善和版本兼容了。
江湖人称『凯子哥』,Android开发者,喜欢技术分享,热爱开源。