转自
http://blog.csdn.net/sbsujjbcy/article/details/50812674
现在,热修复的具体实现方案开源的也有很多,原理也大同小异,本篇文章以Nuwa为例,深入剖析。
Nuwa的github地址
https://github.com/jasonross/Nuwa
以及用于hotpatch生成的gradle插件地址
https://github.com/jasonross/NuwaGradle
而Nuwa的具体实现是根据QQ空间的热修复方案来实现的。安卓App热补丁动态修复技术介绍。在阅读本篇文章之前,请先阅读该文章。
从QQ空间终端开发团队的文章中可以总结出要进行热更新只需要满足下面两点就可以了:
- 动态加载补丁dex,并将补丁dex插入到dexElements最前面
- 要实现热更新,需要热更新的类要防止被打上ISPREVERIFIED标记,关于这个标记,请阅读上面QQ空间团队的文章。
对于第一点,实现很简单,通过DexClassLoader对象,将补丁dex对象加载进来,再通过反射将补丁dex插入到dexElements最前面即可。具体可参考谷歌的Multidex的实现。
而对于第二点,关键就是如何防止类被打上ISPREVERIFIED这个标记。
简单来说,就是将所有类的构造函数中,引用另一个hack.dex中的类,这个类叫Hack.class,然后在加载补丁patch.dex前动态加载这个hack.dex,但是有一个类的构造函数中不能引用Hack.class,这个类就是Application类的子类,一旦这个类的构造函数中加入Hack.class这个类,那么程序运行时就会找不到Hack.class这个类,因为还没有被加载。也就是说,一个类直接引用到的类不在同一个dex中即可。这样,就能防止类被打上ISPREVERIFIED标记并能进行热更新。
我们先来看Nuwa的实现,再去看Nuwa的插件的实现。
使用Nuwa的时候需要在attachBaseContext方法中初始化
<code class="hljs cs 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-radius: 0px; word-wrap: normal; background: transparent;">@Override <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>(Context <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">base</span>) { super.attachBaseContext(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">base</span>); Nuwa.init(<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></ul>
Nuwa预先将Hack.class这个类(空实现)打成apk文件,放在asserts目录中,在init方法中,做的就是将asserts目录中的这个文件拷贝到文件目录下。
<code class="hljs cs 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-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-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;">init</span>(Context context) { File dexDir = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(context.getFilesDir(), DEX_DIR); dexDir.mkdir(); String 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;">try</span> { dexPath = AssetUtils.copyAsset(context, HACK_DEX, dexDir); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (IOException e) { Log.e(TAG, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"copy "</span> + HACK_DEX + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" failed"</span>); e.printStackTrace(); } loadPatch(context, dexPath); }</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></ul>
首先创建文件目录将asserts目录下的hack.apk拷到该目录,然后调用loadPatch方法将该apk动态加载进来。loadPatch方法也是之后进行热修复的关键方法,你的所有补丁文件都是通过这个方法动态加载进来。
<code class="hljs lasso 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-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> static <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">void</span> loadPatch(Context context, <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span> dexPath) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (context <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">==</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">null</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Log</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>e(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">TAG</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"context is 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;">if</span> (<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">!</span><span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">new</span> File(dexPath)<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>exists()) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Log</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>e(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">TAG</span>, dexPath <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">+</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" is null"</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>; } File dexOptDir <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">=</span> <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">new</span> File(context<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>getFilesDir(), DEX_OPT_DIR); dexOptDir<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>mkdir(); try { DexUtils<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>injectDexAtFirst(dexPath, dexOptDir<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>getAbsolutePath()); } catch (Exception e) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Log</span><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>e(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">TAG</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"inject "</span> <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">+</span> dexPath <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">+</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" failed"</span>); e<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>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><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></ul>
loadPatch方法中主要是调用DexUtils.injectDexAtFirst()方法将dex插入到dexElements最前面。该方法如下。
<code class="hljs javascript 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-radius: 0px; word-wrap: normal; background: transparent;">public static <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> injectDexAtFirst(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span> dexPath, <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span> defaultDexOptPath) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { DexClassLoader dexClassLoader = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DexClassLoader(dexPath, defaultDexOptPath, dexPath, getPathClassLoader()); <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Object</span> baseDexElements = getDexElements(getPathList(getPathClassLoader())); <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Object</span> newDexElements = getDexElements(getPathList(dexClassLoader)); <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Object</span> allDexElements = combineArray(newDexElements, baseDexElements); <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Object</span> pathList = getPathList(getPathClassLoader()); ReflectionUtils.setField(pathList, pathList.getClass(), <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"dexElements"</span>, allDexElements); }</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></ul>
根据传入的dex的文件目录defaultDexOptPath,构造DexClassLoader对象dexClassLoader,然后通过getDexElements方法获得原来的dexElements对象,之后拿到dexClassLoader对象中的dexElements对象,调用combineArray方法将这两个对象进行结合,将我们传进来的dex插到该对象的最前面,之后调用ReflectionUtils.setField()方法,将dexElements进行替换。combineArray方法中做的就是扩展数组,将第二个数组插入到第一个数组的最前面
<code class="hljs vbscript 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-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> static Object combineArray(Object firstArray, Object secondArray) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Class</span><?> localClass = firstArray.getClass().getComponentType(); <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">int</span> firstArrayLength = <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Array</span>.getLength(firstArray); <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">int</span> allLength = firstArrayLength + <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Array</span>.getLength(secondArray); Object result = <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Array</span>.newInstance(localClass, allLength); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">int</span> k = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; k < allLength; ++k) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (k < firstArrayLength) { <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Array</span>.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">set</span>(result, k, <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Array</span>.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">get</span>(firstArray, k)); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Array</span>.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">set</span>(result, k, <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">Array</span>.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">get</span>(secondArray, k - firstArrayLength)); } } return result; }</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></ul>
之后如果你有补丁要应用,直接调用Nuwa.loadPatch()方法,传入补丁的目录,重启应用之后就可以进行热更新了。这是Nuwa应用层的实现,可以看到,并不复杂。相对复杂的是Gradle插件层的实现。Gradle插件要做的事就是拿到所有class,在其构造函数中注入Hack.class,使其直接引用另一个dex中的文件,防止被打上ISPREVERIFIED标记。并且混淆的时候要应用上一次release版本的mapping文件。现在有两点关键内容:
- 如何拿到所有的class
- 如何在构造函数中注入代码
我们先来解决第二点,如何注入代码,Nuwa使用的是asm注入代码。
现在假设我们已经存在了hack.apk,并且里面已经有了Hack.class文件,其源代码如下
<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-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> cn.edu.zafu.hotpatch.asm; <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @author</span> lizhangqu *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @since</span> 2016-03-06 10:31 */</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);">Hack</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><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></ul>
我们编写一个测试类Test,里面有一个测试方法,我们需要将Hack.class注入到Test的构造函数中,让其直接引用另一个dex中的类。
<code class="hljs cs 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-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-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> Test { <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;">method1</span>(){ String str=<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"111"</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></ul>
我们编译一下,得到Test.clss,将其复制到一个目录dir。然后终端进入到该目录,使用javap命令查看字节码
可以看到图中有 < init >字样,该处就是构造函数,然后看到4:return,这是构造函数的结束的地方。现在我们读入该文件,并对其进行字节码修改,然后写入该目录下dest目录下。在这之前,需要加入asm的依赖,至于asm的使用,请自行查询。
<code class="hljs bash 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-radius: 0px; word-wrap: normal; background: transparent;"> compile <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'org.ow2.asm:asm:5.0.4'</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>
我们先将该文件读入,获得输入流,调用referHackWhenInit方法,将输入流传入,用ClassVisitor对象访问该对象,实现MethodVisitor方法,在该方法中访问对象中的方法,对方法名进行判断,如果是构造函数,则对其进行字节码注入操作,接下来运行main方法,查看dest目录下生成的文件。
<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-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);">Main</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;">main</span>(String[] args) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> IOException { File srcFile = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/Users/lizhangqu/AndroidStudioProjects/Hotpatch/bak/Test.class"</span>); File destDir = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/Users/lizhangqu/AndroidStudioProjects/Hotpatch/bak/dest/"</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!destDir.exists()) { destDir.mkdirs(); } InputStream is = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> FileInputStream(srcFile); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] bytes = referHackWhenInit(is); File destFile = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(destDir, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Test.class"</span>); FileOutputStream fos = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> FileOutputStream(destFile); fos.write(bytes); fos.close(); } <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;">byte</span>[] <span class="hljs-title" style="box-sizing: border-box;">referHackWhenInit</span>(InputStream inputStream) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> IOException { ClassReader cr = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ClassReader(inputStream); ClassWriter cw = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ClassWriter(cr, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>); ClassVisitor cv = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ClassVisitor(Opcodes.ASM4, cw) { <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> MethodVisitor <span class="hljs-title" style="box-sizing: border-box;">visitMethod</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> access, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.visitMethod(access, name, desc, signature, exceptions); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"<init>"</span>.equals(name)) { mv = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> MethodVisitor(Opcodes.ASM4, mv) { <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;">visitInsn</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> opcode) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (opcode == Opcodes.RETURN) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.visitLdcInsn(Type.getType(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Lcn/edu/zafu/hotpatch/asm/Hack"</span>)); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.visitInsn(opcode); } }; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> mv; } }; cr.accept(cv, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> cw.toByteArray(); } } </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></ul>
生成的Test.class文件内容如下
可以看到构造函数中直接引用了Hack.class,然后我们使用javap命令查看字节码
可以看到return之前,插入了我们的字节码,直接引用了Hack.class
字节码注入的问题解决了,接下来就是找到要注入字节码的所有class。
接下来分析Nuwa的Gradle插件,在分析之前,请先了解一下Gralde插件的开发流程,可以阅读这篇文章如何使用Android Studio开发Gradle插件。下面的内容的gradle版本是基于1.2.3,高版本的可能有所差异。请查看项目依赖的是否是下面的这个版本
<code class="hljs bash 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-radius: 0px; word-wrap: normal; background: transparent;">classpath <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'com.android.tools.build:gradle:1.2.3'</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>
为了找到这些class,实际上,分为了两种情况
- 开启了Multidex的项目
- 没有开启Multidex的项目
如果使用了MultiDex,并且没有混淆,这种情况很简单,dex任务之前会生成一个jar文件,包含了所有的class,所以做起来很容易。但是如果添加了混淆怎么办?试了一下,也是proguard后也是生成了一个jar包,也没啥问题
为了验证作者的论证,我们编写一个插件来验证一下,关于如何编写插件,请查看上面贴的文章。
我们先在项目中开启Multidex
<code class="hljs bash 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-radius: 0px; word-wrap: normal; background: transparent;">multiDexEnabled <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">true</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>
对于release的构建,开启混淆,对于debug,关闭混淆
<code class="hljs lua 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-radius: 0px; word-wrap: normal; background: transparent;">buildTypes { release { minifyEnabled <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span> proguardFiles getDefaultProguardFile(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'proguard-android.txt'</span>), <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'proguard-rules.pro'</span> } <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">debug</span>{ minifyEnabled <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</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></ul>
这个插件的作用是什么的,其实很简单,就是输出preDex,dex,proguard这三个Task的输入文件,当然前提是Task存在。代码如下
<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-radius: 0px; word-wrap: normal; background: transparent;">public class PluginImpl implements Plugin<Project> { public void apply(Project project) { project<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.afterEvaluate</span> { project<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.android</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.applicationVariants</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.each</span> { variant -> def preDexTask = project<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.tasks</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.findByName</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"preDex${variant.name.capitalize()}"</span>) def dexTask = project<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.tasks</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.findByName</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"dex${variant.name.capitalize()}"</span>) def proguardTask = project<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.tasks</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.findByName</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"proguard${variant.name.capitalize()}"</span>) if (preDexTask) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Set</span><File> preDexTaskInputFiles = preDexTask<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.inputs</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.files</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.files</span> project<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.logger</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.error</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Name:preDexTaskInputFiles=====>${preDexTask.name}"</span> preDexTaskInputFiles<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.each</span> { inputFile -> def path = inputFile<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.absolutePath</span> project<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.logger</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.error</span> path } } if (dexTask) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Set</span><File> dexTaskInputFiles = dexTask<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.inputs</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.files</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.files</span> project<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.logger</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.error</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Name:dexTaskInputFiles=====>${dexTask.name}"</span> dexTaskInputFiles<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.each</span> { inputFile -> def path = inputFile<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.absolutePath</span> project<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.logger</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.error</span> path } } if (proguardTask) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Set</span><File> proguardTaskInputFiles = proguardTask<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.inputs</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.files</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.files</span> project<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.logger</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.error</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Name:proguardTask=====>${proguardTask.name}"</span> proguardTaskInputFiles<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.each</span> { inputFile -> def path = inputFile<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.absolutePath</span> project<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.logger</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.error</span> path } } } } } }</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></ul>
应用插件然后查看插件输出的日志
可以看到,对于debug的构建,我们没有开启混淆,dex的Task的输入文件是一个allclasses.jar,而release版本的构建,dex的Task的输入文件是混淆之后的文件classes.jar。并且无论是debug还是release,对于这种开启了Multidex的情况下,是不存在preDex这个Task的,对于这种情况,我们可以判断preDex这个Task是否存在进行操作。查看NuwaGradle的源码。相关解释,我已经加入到注释中了。
<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-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 了解到preDex会在dex任务之前把所有的库工程和第三方jar包提前打成dex, * 下次运行只需重新dex被修改的库,以此节省时间。 * dex任务会把preDex生成的dex文件和主工程中的class文件一起生成class.dex */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (preDexTask){ <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//这个Task存在的情况,即没有开启Multidex</span> }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 如果preDexTask这个task不存在,即开启了Multidex * dex任务之前会生成一个jar文件,包含了所有的class,即使做了混淆也是一个jar * 这种情况下只对jar进行处理即可 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> nuwaJarBeforeDex = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"nuwaJarBeforeDex${variant.name.capitalize()}"</span> project.task(nuwaJarBeforeDex) << { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 获得所有输入文件 */</span> Set<File> inputFiles = dexTask.inputs.files.files <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 遍历所有文件 */</span> inputFiles.each { inputFile -> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> path = inputFile.absolutePath <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 如果是以jar结尾,则对jar进行字节码注入处理 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (path.endsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">".jar"</span>)) { NuwaProcessor.processJar(hashFile, inputFile, patchDir, hashMap, includePackage, excludeClass) } } } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 处理task依赖 * nuwaJarBeforeDexTask在dexTask之前,在dexTask原来之前所有task之后 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> nuwaJarBeforeDexTask = project.tasks[nuwaJarBeforeDex] nuwaJarBeforeDexTask.dependsOn dexTask.taskDependencies.getDependencies(dexTask) dexTask.dependsOn nuwaJarBeforeDexTask <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * nuwaJarBeforeDexTask开始时执行nuwaPrepareClosure闭包 * 这个闭包做的就是创建文件夹等初始化话操作 * 结束时执行copyMappingClosure拷贝mapping文件和hash.txt文件 */</span> nuwaJarBeforeDexTask.doFirst(nuwaPrepareClosure) nuwaJarBeforeDexTask.doLast(copyMappingClosure) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * patch的dex生成是在=》操作依赖字节码修改之后的task执行完毕后再执行 */</span> nuwaPatchTask.dependsOn nuwaJarBeforeDexTask beforeDexTasks.add(nuwaJarBeforeDexTask) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 以上task的总结 * dex之前的所有task->获得dex之前的所有输入文件->字节码注入->dex */</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></ul>
而上面使用到了nuwaPrepareClosure和copyMappingClosure这两个闭包。以及Gradle插件的初始化操作如下,详情见注释
<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-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * hash值对应的map */</span> Map hashMap <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * nuwa的输出产物目录 */</span> File nuwaDir <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 需要打patch的classes文件目录,会对比hash值,如果hash值不一样,会拷到这个目录 */</span> File patchDir <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 找到preDex,dex,proguard这三个task */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> preDexTask = project.tasks.findByName(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"preDex${variant.name.capitalize()}"</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> dexTask = project.tasks.findByName(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"dex${variant.name.capitalize()}"</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> proguardTask = project.tasks.findByName(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"proguard${variant.name.capitalize()}"</span>) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 找到manifest文件 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> processManifestTask = project.tasks.findByName(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"process${variant.name.capitalize()}Manifest"</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> manifestFile = processManifestTask.outputs.files.files[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>] <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 这个属性是从控制台输入的,代表之前release版本生成的混淆文件和hash文件目录,这两个文件发版时需要保持 * ./gradlew clean nuwaQihooDebugPatch -P NuwaDir=/Users/jason/Documents/nuwa */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> oldNuwaDir = NuwaFileUtils.getFileFromProperty(project, NUWA_DIR) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (oldNuwaDir) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 如果文件夹存在的话混淆的时候应用mapping */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> mappingFile = NuwaFileUtils.getVariantFile(oldNuwaDir, variant, MAPPING_TXT) NuwaAndroidUtils.applymapping(proguardTask, mappingFile) } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (oldNuwaDir) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 如果文件夹存在的话获得各个class的hash */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> hashFile = NuwaFileUtils.getVariantFile(oldNuwaDir, variant, HASH_TXT) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 将文件中的hash存入这个map */</span> hashMap = NuwaMapUtils.parseMap(hashFile) } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * /qihoo/debug */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> dirName = variant.dirName <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * /build/outputs/nuwa/ */</span> nuwaDir = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${project.buildDir}/outputs/nuwa"</span>) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 不同variant对应nuwa目录下不同文件夹 * /build/outputs/nuwa/qihoo/debug */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> outputDir = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${nuwaDir}/${dirName}"</span>) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * hash文件 * /build/outputs/nuwa/qihoo/debug/hash.txt */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> hashFile = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(outputDir, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"hash.txt"</span>) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 创建相关文件的闭包 */</span> Closure nuwaPrepareClosure = { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 获得application类 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> applicationName = NuwaAndroidUtils.getApplication(manifestFile) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (applicationName != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 如果已经定义了application类,则加入excludeClass列表,不执行字节码修改 */</span> excludeClass.add(applicationName) } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 创建对应的文件夹及hash文件 */</span> outputDir.mkdirs() <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!hashFile.exists()) { hashFile.createNewFile() } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 创建patch文件夹 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (oldNuwaDir) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 此目录存patch的classes * /build/outputs/nuwa/qihoo/debug/patch/ */</span> patchDir = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${nuwaDir}/${dirName}/patch"</span>) patchDir.mkdirs() patchList.add(patchDir) } } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 注入nuwaPatch的task * nuwaQihooDebugPatch */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> nuwaPatch = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"nuwa${variant.name.capitalize()}Patch"</span> project.task(nuwaPatch) << { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (patchDir) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 执行patch的dex操作 */</span> NuwaAndroidUtils.dex(project, patchDir) } } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 获得打patch的task */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> nuwaPatchTask = project.tasks[nuwaPatch] <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 拷贝mapping的闭包 */</span> Closure copyMappingClosure = { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 将构建产生的mapping文件拷贝至目标nuwa目录 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (proguardTask) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> mapFile = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${project.buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> newMapFile = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${nuwaDir}/${variant.dirName}/mapping.txt"</span>); FileUtils.copyFile(mapFile, newMapFile) } }</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><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li><li style="box-sizing: border-box; padding: 0px 5px;">84</li><li style="box-sizing: border-box; padding: 0px 5px;">85</li><li style="box-sizing: border-box; padding: 0px 5px;">86</li><li style="box-sizing: border-box; padding: 0px 5px;">87</li><li style="box-sizing: border-box; padding: 0px 5px;">88</li><li style="box-sizing: border-box; padding: 0px 5px;">89</li><li style="box-sizing: border-box; padding: 0px 5px;">90</li><li style="box-sizing: border-box; padding: 0px 5px;">91</li><li style="box-sizing: border-box; padding: 0px 5px;">92</li><li style="box-sizing: border-box; padding: 0px 5px;">93</li><li style="box-sizing: border-box; padding: 0px 5px;">94</li><li style="box-sizing: border-box; padding: 0px 5px;">95</li><li style="box-sizing: border-box; padding: 0px 5px;">96</li><li style="box-sizing: border-box; padding: 0px 5px;">97</li><li style="box-sizing: border-box; padding: 0px 5px;">98</li><li style="box-sizing: border-box; padding: 0px 5px;">99</li><li style="box-sizing: border-box; padding: 0px 5px;">100</li><li style="box-sizing: border-box; padding: 0px 5px;">101</li><li style="box-sizing: border-box; padding: 0px 5px;">102</li><li style="box-sizing: border-box; padding: 0px 5px;">103</li><li style="box-sizing: border-box; padding: 0px 5px;">104</li><li style="box-sizing: border-box; padding: 0px 5px;">105</li><li style="box-sizing: border-box; padding: 0px 5px;">106</li><li style="box-sizing: border-box; padding: 0px 5px;">107</li><li style="box-sizing: border-box; padding: 0px 5px;">108</li><li style="box-sizing: border-box; padding: 0px 5px;">109</li><li style="box-sizing: border-box; padding: 0px 5px;">110</li><li style="box-sizing: border-box; padding: 0px 5px;">111</li><li style="box-sizing: border-box; padding: 0px 5px;">112</li><li style="box-sizing: border-box; padding: 0px 5px;">113</li><li style="box-sizing: border-box; padding: 0px 5px;">114</li><li style="box-sizing: border-box; padding: 0px 5px;">115</li><li style="box-sizing: border-box; padding: 0px 5px;">116</li><li style="box-sizing: border-box; padding: 0px 5px;">117</li><li style="box-sizing: border-box; padding: 0px 5px;">118</li><li style="box-sizing: border-box; padding: 0px 5px;">119</li><li style="box-sizing: border-box; padding: 0px 5px;">120</li><li style="box-sizing: border-box; padding: 0px 5px;">121</li><li style="box-sizing: border-box; padding: 0px 5px;">122</li><li style="box-sizing: border-box; padding: 0px 5px;">123</li><li style="box-sizing: border-box; padding: 0px 5px;">124</li><li style="box-sizing: border-box; padding: 0px 5px;">125</li><li style="box-sizing: border-box; padding: 0px 5px;">126</li><li style="box-sizing: border-box; padding: 0px 5px;">127</li><li style="box-sizing: border-box; padding: 0px 5px;">128</li><li style="box-sizing: border-box; padding: 0px 5px;">129</li><li style="box-sizing: border-box; padding: 0px 5px;">130</li><li style="box-sizing: border-box; padding: 0px 5px;">131</li><li style="box-sizing: border-box; padding: 0px 5px;">132</li><li style="box-sizing: border-box; padding: 0px 5px;">133</li><li style="box-sizing: border-box; padding: 0px 5px;">134</li></ul>
而对于没有开启Multidex的情况,则会存在一个preDex的Task。preDex会在dex任务之前把所有的库工程和第三方jar包提前打成dex,下次运行只需重新dex被修改的库,以此节省时间。dex任务会把preDex生成的dex文件和主工程中的class文件一起生成class.dex,这样就需要针对有无preDex,做不同的修改字节码策略即可。源码解释如下。
<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-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 了解到preDex会在dex任务之前把所有的库工程和第三方jar包提前打成dex, * 下次运行只需重新dex被修改的库,以此节省时间。 * dex任务会把preDex生成的dex文件和主工程中的class文件一起生成class.dex */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (preDexTask) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 处理jar文件,这些jar是所有的库工程和第三方jar包,是preDexTask的输入文件 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> nuwaJarBeforePreDex = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"nuwaJarBeforePreDex${variant.name.capitalize()}"</span> project.task(nuwaJarBeforePreDex) << { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 获得preDex的所有jar文件 */</span> Set<File> inputFiles = preDexTask.inputs.files.files <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 遍历jar文件 */</span> inputFiles.each { inputFile -> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> path = inputFile.absolutePath <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 如果是以classes.jar结尾的文件并且路径中不包含com.android.support且路径中中不包含/android/m2repository */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (NuwaProcessor.shouldProcessPreDexJar(path)) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 处理classes.jar,注入字节码 */</span> NuwaProcessor.processJar(hashFile, inputFile, patchDir, hashMap, includePackage, excludeClass) } } } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 处理task依赖 * nuwaJarBeforePreDexTask依赖preDexTask之前所有的task * preDexTask依赖nuwaJarBeforePreDexTask */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> nuwaJarBeforePreDexTask = project.tasks[nuwaJarBeforePreDex] nuwaJarBeforePreDexTask.dependsOn preDexTask.taskDependencies.getDependencies(preDexTask) preDexTask.dependsOn nuwaJarBeforePreDexTask <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 这个task之前进行这个闭包处理,主要做创建文件的操作 */</span> nuwaJarBeforePreDexTask.doFirst(nuwaPrepareClosure) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 处理classes文件,注意这里是主工程的class文件,是dexTask的输入文件 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> nuwaClassBeforeDex = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"nuwaClassBeforeDex${variant.name.capitalize()}"</span> project.task(nuwaClassBeforeDex) << { Set<File> inputFiles = dexTask.inputs.files.files <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 遍历所有class文件 */</span> inputFiles.each { inputFile -> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> path = inputFile.absolutePath <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 以class结尾,不包含R路径,不是R.class,不是BuildConfig.class文件 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (path.endsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">".class"</span>) && !path.contains(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/R\$"</span>) && !path.endsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/R.class"</span>) && !path.endsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/BuildConfig.class"</span>)) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 包含在includePackage内 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (NuwaSetUtils.isIncluded(path, includePackage)) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 不包含在excludeClass内 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!NuwaSetUtils.isExcluded(path, excludeClass)) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 往class中注入字节码 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> bytes = NuwaProcessor.processClass(inputFile) path = path.split(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${dirName}/"</span>)[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>] <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * hash校验 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> hash = DigestUtils.shaHex(bytes) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 往hash.txt文件中写入hash值 */</span> hashFile.append(NuwaMapUtils.format(path, hash)) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 与上一个release版本hash值不一样则复制出来,作为patch.jar的组成部分 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (NuwaMapUtils.notSame(hashMap, path, hash)) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 拷贝到patch目录 */</span> NuwaFileUtils.copyBytesToFile(inputFile.bytes, NuwaFileUtils.touchFile(patchDir, path)) } } } } } } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 重新处理task依赖关系 * nuwaClassBeforeDexTask依赖dexTask这个task之前依赖的所有Task * dexTask这个Task依赖 nuwaClassBeforeDexTask这个Task */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> nuwaClassBeforeDexTask = project.tasks[nuwaClassBeforeDex] nuwaClassBeforeDexTask.dependsOn dexTask.taskDependencies.getDependencies(dexTask) dexTask.dependsOn nuwaClassBeforeDexTask <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 最后拷贝mapping文件备份 */</span> nuwaClassBeforeDexTask.doLast(copyMappingClosure) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * patch的dex操作依赖字节码修改之后的task,即nuwaClassBeforeDexTask */</span> nuwaPatchTask.dependsOn nuwaClassBeforeDexTask beforeDexTasks.add(nuwaClassBeforeDexTask) }</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><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li><li style="box-sizing: border-box; padding: 0px 5px;">84</li><li style="box-sizing: border-box; padding: 0px 5px;">85</li><li style="box-sizing: border-box; padding: 0px 5px;">86</li><li style="box-sizing: border-box; padding: 0px 5px;">87</li><li style="box-sizing: border-box; padding: 0px 5px;">88</li><li style="box-sizing: border-box; padding: 0px 5px;">89</li><li style="box-sizing: border-box; padding: 0px 5px;">90</li><li style="box-sizing: border-box; padding: 0px 5px;">91</li><li style="box-sizing: border-box; padding: 0px 5px;">92</li><li style="box-sizing: border-box; padding: 0px 5px;">93</li><li style="box-sizing: border-box; padding: 0px 5px;">94</li><li style="box-sizing: border-box; padding: 0px 5px;">95</li><li style="box-sizing: border-box; padding: 0px 5px;">96</li><li style="box-sizing: border-box; padding: 0px 5px;">97</li><li style="box-sizing: border-box; padding: 0px 5px;">98</li><li style="box-sizing: border-box; padding: 0px 5px;">99</li><li style="box-sizing: border-box; padding: 0px 5px;">100</li><li style="box-sizing: border-box; padding: 0px 5px;">101</li><li style="box-sizing: border-box; padding: 0px 5px;">102</li><li style="box-sizing: border-box; padding: 0px 5px;">103</li><li style="box-sizing: border-box; padding: 0px 5px;">104</li><li style="box-sizing: border-box; padding: 0px 5px;">105</li><li style="box-sizing: border-box; padding: 0px 5px;">106</li><li style="box-sizing: border-box; padding: 0px 5px;">107</li><li style="box-sizing: border-box; padding: 0px 5px;">108</li><li style="box-sizing: border-box; padding: 0px 5px;">109</li><li style="box-sizing: border-box; padding: 0px 5px;">110</li><li style="box-sizing: border-box; padding: 0px 5px;">111</li><li style="box-sizing: border-box; padding: 0px 5px;">112</li><li style="box-sizing: border-box; padding: 0px 5px;">113</li><li style="box-sizing: border-box; padding: 0px 5px;">114</li><li style="box-sizing: border-box; padding: 0px 5px;">115</li><li style="box-sizing: border-box; padding: 0px 5px;">116</li></ul>
这样就完成了字节码的修改,至于字节码修改的函数,其实就和最开始的测试asm修改字节码的例子差不多,对于jar文件,需要将jar文件中的所有class遍历一遍处理。字节码的注入操作全在NuwaProcessor这个类中。源码解析如下
<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-radius: 0px; word-wrap: normal; background: transparent;">class NuwaProcessor { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 处理jar *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> hashFile *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> jarFile *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> patchDir *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> map *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> includePackage *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> excludeClass *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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-title" style="box-sizing: border-box;">processJar</span>(File hashFile, File jarFile, File patchDir, Map map, HashSet<String> includePackage, HashSet<String> excludeClass) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (jarFile) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * classes.jar dex后的文件 */</span> def optJar = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(jarFile.getParent(), jarFile.name + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">".opt"</span>) def file = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> JarFile(jarFile); Enumeration enumeration = file.entries(); JarOutputStream jarOutputStream = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> JarOutputStream(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> FileOutputStream(optJar)); <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 枚举jar文件中的所有文件 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (enumeration.hasMoreElements()) { JarEntry jarEntry = (JarEntry) enumeration.nextElement(); String entryName = jarEntry.getName(); ZipEntry zipEntry = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ZipEntry(entryName); InputStream inputStream = file.getInputStream(jarEntry); jarOutputStream.putNextEntry(zipEntry); <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 以class结尾的文件并且在include中不在exclude中,并且不是cn/jiajixin/nuwa/包中的文件 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (shouldProcessClassInJar(entryName, includePackage, excludeClass)) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 构造函数中注入字节码 */</span> def bytes = referHackWhenInit(inputStream); <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 写入子杰 */</span> jarOutputStream.write(bytes); <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * hash校验 */</span> def hash = DigestUtils.shaHex(bytes) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 加入hash值 */</span> hashFile.append(NuwaMapUtils.format(entryName, hash)) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * hash值与上一release版本不一样则拷到对应的目录,作为patch的类 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (NuwaMapUtils.notSame(map, entryName, hash)) { NuwaFileUtils.copyBytesToFile(bytes, NuwaFileUtils.touchFile(patchDir, entryName)) } } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 否则直接输出文件不处理 */</span> jarOutputStream.write(IOUtils.toByteArray(inputStream)); } jarOutputStream.closeEntry(); } jarOutputStream.close(); file.close(); <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 删除jar文件 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (jarFile.exists()) { jarFile.delete() } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * dex后的文件重命名为jar文件 */</span> optJar.renameTo(jarFile) } } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//refer hack class when object init</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;">byte</span>[] <span class="hljs-title" style="box-sizing: border-box;">referHackWhenInit</span>(InputStream inputStream) { ClassReader cr = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ClassReader(inputStream); ClassWriter cw = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ClassWriter(cr, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>); ClassVisitor cv = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ClassVisitor(Opcodes.ASM4, cw) { <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> MethodVisitor <span class="hljs-title" style="box-sizing: border-box;">visitMethod</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.visitMethod(access, name, desc, signature, exceptions); mv = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> MethodVisitor(Opcodes.ASM4, mv) { <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;">void</span> visitInsn(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> opcode) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 如果是构造函数 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"<init>"</span>.equals(name) && opcode == Opcodes.RETURN) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 注入代码 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.visitLdcInsn(Type.getType(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Lcn/jiajixin/nuwa/Hack;"</span>)); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.visitInsn(opcode); } } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> mv; } }; cr.accept(cv, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> cw.toByteArray(); } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 是否需要在preDex前处理 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> path *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">shouldProcessPreDexJar</span>(String path) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> path.endsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"classes.jar"</span>) && !path.contains(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"com.android.support"</span>) && !path.contains(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/android/m2repository"</span>); } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * jar中的文件是否需要处理 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> entryName *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> includePackage *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> excludeClass *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">shouldProcessClassInJar</span>(String entryName, HashSet<String> includePackage, HashSet<String> excludeClass) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> entryName.endsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">".class"</span>) && !entryName.startsWith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"cn/jiajixin/nuwa/"</span>) && NuwaSetUtils.isIncluded(entryName, includePackage) && !NuwaSetUtils.isExcluded(entryName, excludeClass) && !entryName.contains(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"android/support/"</span>) } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 处理class *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> file *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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;">byte</span>[] <span class="hljs-title" style="box-sizing: border-box;">processClass</span>(File file) { def optClass = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(file.getParent(), file.name + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">".opt"</span>) FileInputStream inputStream = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> FileInputStream(file); FileOutputStream outputStream = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> FileOutputStream(optClass) <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 对class注入字节码 */</span> def bytes = referHackWhenInit(inputStream); outputStream.write(bytes) inputStream.close() outputStream.close() <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (file.exists()) { file.delete() } optClass.renameTo(file) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> bytes } }</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><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li><li style="box-sizing: border-box; padding: 0px 5px;">84</li><li style="box-sizing: border-box; padding: 0px 5px;">85</li><li style="box-sizing: border-box; padding: 0px 5px;">86</li><li style="box-sizing: border-box; padding: 0px 5px;">87</li><li style="box-sizing: border-box; padding: 0px 5px;">88</li><li style="box-sizing: border-box; padding: 0px 5px;">89</li><li style="box-sizing: border-box; padding: 0px 5px;">90</li><li style="box-sizing: border-box; padding: 0px 5px;">91</li><li style="box-sizing: border-box; padding: 0px 5px;">92</li><li style="box-sizing: border-box; padding: 0px 5px;">93</li><li style="box-sizing: border-box; padding: 0px 5px;">94</li><li style="box-sizing: border-box; padding: 0px 5px;">95</li><li style="box-sizing: border-box; padding: 0px 5px;">96</li><li style="box-sizing: border-box; padding: 0px 5px;">97</li><li style="box-sizing: border-box; padding: 0px 5px;">98</li><li style="box-sizing: border-box; padding: 0px 5px;">99</li><li style="box-sizing: border-box; padding: 0px 5px;">100</li><li style="box-sizing: border-box; padding: 0px 5px;">101</li><li style="box-sizing: border-box; padding: 0px 5px;">102</li><li style="box-sizing: border-box; padding: 0px 5px;">103</li><li style="box-sizing: border-box; padding: 0px 5px;">104</li><li style="box-sizing: border-box; padding: 0px 5px;">105</li><li style="box-sizing: border-box; padding: 0px 5px;">106</li><li style="box-sizing: border-box; padding: 0px 5px;">107</li><li style="box-sizing: border-box; padding: 0px 5px;">108</li><li style="box-sizing: border-box; padding: 0px 5px;">109</li><li style="box-sizing: border-box; padding: 0px 5px;">110</li><li style="box-sizing: border-box; padding: 0px 5px;">111</li><li style="box-sizing: border-box; padding: 0px 5px;">112</li><li style="box-sizing: border-box; padding: 0px 5px;">113</li><li style="box-sizing: border-box; padding: 0px 5px;">114</li><li style="box-sizing: border-box; padding: 0px 5px;">115</li><li style="box-sizing: border-box; padding: 0px 5px;">116</li><li style="box-sizing: border-box; padding: 0px 5px;">117</li><li style="box-sizing: border-box; padding: 0px 5px;">118</li><li style="box-sizing: border-box; padding: 0px 5px;">119</li><li style="box-sizing: border-box; padding: 0px 5px;">120</li><li style="box-sizing: border-box; padding: 0px 5px;">121</li><li style="box-sizing: border-box; padding: 0px 5px;">122</li><li style="box-sizing: border-box; padding: 0px 5px;">123</li><li style="box-sizing: border-box; padding: 0px 5px;">124</li><li style="box-sizing: border-box; padding: 0px 5px;">125</li><li style="box-sizing: border-box; padding: 0px 5px;">126</li><li style="box-sizing: border-box; padding: 0px 5px;">127</li><li style="box-sizing: border-box; padding: 0px 5px;">128</li><li style="box-sizing: border-box; padding: 0px 5px;">129</li><li style="box-sizing: border-box; padding: 0px 5px;">130</li><li style="box-sizing: border-box; padding: 0px 5px;">131</li><li style="box-sizing: border-box; padding: 0px 5px;">132</li><li style="box-sizing: border-box; padding: 0px 5px;">133</li><li style="box-sizing: border-box; padding: 0px 5px;">134</li><li style="box-sizing: border-box; padding: 0px 5px;">135</li><li style="box-sizing: border-box; padding: 0px 5px;">136</li><li style="box-sizing: border-box; padding: 0px 5px;">137</li><li style="box-sizing: border-box; padding: 0px 5px;">138</li><li style="box-sizing: border-box; padding: 0px 5px;">139</li><li style="box-sizing: border-box; padding: 0px 5px;">140</li><li style="box-sizing: border-box; padding: 0px 5px;">141</li><li style="box-sizing: border-box; padding: 0px 5px;">142</li><li style="box-sizing: border-box; padding: 0px 5px;">143</li><li style="box-sizing: border-box; padding: 0px 5px;">144</li><li style="box-sizing: border-box; padding: 0px 5px;">145</li><li style="box-sizing: border-box; padding: 0px 5px;">146</li><li style="box-sizing: border-box; padding: 0px 5px;">147</li><li style="box-sizing: border-box; padding: 0px 5px;">148</li><li style="box-sizing: border-box; padding: 0px 5px;">149</li><li style="box-sizing: border-box; padding: 0px 5px;">150</li><li style="box-sizing: border-box; padding: 0px 5px;">151</li><li style="box-sizing: border-box; padding: 0px 5px;">152</li><li style="box-sizing: border-box; padding: 0px 5px;">153</li><li style="box-sizing: border-box; padding: 0px 5px;">154</li><li style="box-sizing: border-box; padding: 0px 5px;">155</li><li style="box-sizing: border-box; padding: 0px 5px;">156</li><li style="box-sizing: border-box; padding: 0px 5px;">157</li><li style="box-sizing: border-box; padding: 0px 5px;">158</li><li style="box-sizing: border-box; padding: 0px 5px;">159</li><li style="box-sizing: border-box; padding: 0px 5px;">160</li></ul>
字节码的注入需要将Application类排除在外,这个类如果在Manifest文件中设置了,我们需要将其拿到,并加入到excludeClass中,这个操作在nuwaPrepareClosure闭包中已经处理了。
<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-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 获得application的名字 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> manifestFile *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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> String <span class="hljs-title" style="box-sizing: border-box;">getApplication</span>(File manifestFile) { def manifest = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> XmlParser().parse(manifestFile) def androidTag = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> groovy.xml.Namespace(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"http://schemas.android.com/apk/res/android"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'android'</span>) def applicationName = manifest.application[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>].attribute(androidTag.name) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (applicationName != <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> applicationName.replace(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"."</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/"</span>) + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">".class"</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;">null</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></ul>
然后如果开启了混淆,我们需要应用上一次发release版本的mapping文件进行混淆
<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-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 混淆时使用上次发版的mapping文件 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> proguardTask *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> mappingFile *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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-title" style="box-sizing: border-box;">applymapping</span>(DefaultTask proguardTask, File mappingFile) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (proguardTask) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mappingFile.exists()) { proguardTask.applymapping(mappingFile) } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { println <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"$mappingFile does not exist"</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></ul>
Nuwa除了支持某个构建执行打Patch的操作之外,还支持批量生产所有构建的Patch,该Task的名字为nuwaPatches,这个Task的依赖关系还要处理一下
<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-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 下面是nuwaPatches的处理,即所有的构建都打patch */</span> <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * nuwaPatches执行dex操作 */</span> project.task(NUWA_PATCHES) << { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 对需要patch的classes执行dex操作 */</span> patchList.each { patchDir -> NuwaAndroidUtils.dex(project, patchDir) } } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 处理依赖nuwaPatches这个Task的依赖,也就是注入字节码的Task之后执行dex操作 */</span> beforeDexTasks.each { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 打patch的task依赖这些task */</span> project.tasks[NUWA_PATCHES].dependsOn it }</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></ul>
所有需要注入字节码的类处理完毕后,我们需要将其进行dex操作,使其能够运行在Android系统上。这个方法在NuwaAndroidUtils类中。
<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-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 对jar进行dex操作 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> project *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> classDir *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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-title" style="box-sizing: border-box;">dex</span>(Project project, File classDir) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (classDir.listFiles().size()) { def sdkDir <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 获得sdk目录 */</span> Properties properties = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Properties() File localProps = project.rootProject.file(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"local.properties"</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (localProps.exists()) { properties.load(localProps.newDataInputStream()) sdkDir = properties.getProperty(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"sdk.dir"</span>) } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { sdkDir = System.getenv(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"ANDROID_HOME"</span>) } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (sdkDir) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 如果是windows系统,加入后缀.bat */</span> def cmdExt = Os.isFamily(Os.FAMILY_WINDOWS) ? <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'.bat'</span> : <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">''</span> def stdout = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ByteArrayOutputStream() <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 拼接命令 * dx --dex --output=patch.jar classDir * classDir是注入字节码后的补丁目录 */</span> project.exec { commandLine <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${sdkDir}/build-tools/${project.android.buildToolsVersion}/dx${cmdExt}"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'--dex'</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"--output=${new File(classDir.getParent(), PATCH_NAME).absolutePath}"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${classDir.absolutePath}"</span> standardOutput = stdout } def error = stdout.toString().trim() <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (error) { println <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"dex error:"</span> + error } } <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;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> InvalidUserDataException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'$ANDROID_HOME is not defined'</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></ul>
还有一个hash值的工具类,包括从文件中解析hash值到map,将hash值格式化写入文件,判断hash值知否一样。
<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-radius: 0px; word-wrap: normal; background: transparent;">class NuwaMapUtils { <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 MAP_SEPARATOR = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">":"</span> <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 判断hash值是否一样 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> map *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> name *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> hash *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">notSame</span>(Map map, String name, String hash) { def notSame = <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> (map) { def value = map.get(name) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (value) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!value.equals(hash)) { notSame = <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;">else</span> { notSame = <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;">return</span> notSame } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 从hash.txt文件中解析内容到map *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> hashFile *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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> Map <span class="hljs-title" style="box-sizing: border-box;">parseMap</span>(File hashFile) { def hashMap = [:] <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (hashFile.exists()) { hashFile.eachLine { List list = it.split(MAP_SEPARATOR) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (list.size() == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>) { hashMap.put(list[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>], list[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>]) } } } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { println <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"$hashFile does not exist"</span> } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> hashMap } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 根据传入的键值对其进行格式化(用:分割) *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> path *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> hash *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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-title" style="box-sizing: border-box;">format</span>(String path, String hash) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> path + MAP_SEPARATOR + hash + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n"</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></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-radius: 0px; word-wrap: normal; background: transparent;">class NuwaFileUtils { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 创建文件 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> dir *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> path *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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> File <span class="hljs-title" style="box-sizing: border-box;">touchFile</span>(File dir, String path) { def file = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${dir}/${path}"</span>) file.getParentFile().mkdirs() <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> file } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 写入字节码到文件 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> bytes *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> file *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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-title" style="box-sizing: border-box;">copyBytesToFile</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] bytes, File file) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!file.exists()) { file.createNewFile() } FileUtils.writeByteArrayToFile(file, bytes) } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 获得控制台传入的属性对应的文件夹 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> project *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> property *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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> File <span class="hljs-title" style="box-sizing: border-box;">getFileFromProperty</span>(Project project, String property) { def file <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (project.hasProperty(property)) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * ./gradlew clean nuwaQihooDebugPatch -P NuwaDir=/Users/jason/Documents/nuwa * 获得NuwaDir对应的目录,即上次发包的mapping和hash文件所在目录 */</span> file = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(project.getProperties()[property]) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!file.exists()) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 文件夹不存在扔异常 */</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> InvalidUserDataException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${project.getProperties()[property]} does not exist"</span>) } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!file.isDirectory()) { <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 不是目录扔异常 */</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> InvalidUserDataException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${project.getProperties()[property]} is not directory"</span>) } } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> file } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 获得不同variant对应的目录下的文件 * 如/qihoo/debug *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> dir *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> variant *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> fileName *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</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> File <span class="hljs-title" style="box-sizing: border-box;">getVariantFile</span>(File dir, def variant, String fileName) { <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;">new</span> File(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"${dir}/${variant.dirName}/${fileName}"</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></ul>
最后,总结一下NuwaGradle的流程。
- 首先判断preDex这个Task是否存在
- 如果不存在,则对dex的输入文件进行遍历,这些输入文件是一系列的jar,对这些jar进行判断,看其是否满足注入字节码的条件,如果满足,对jar文件中满足条件的class文件进行遍历注入字节码,然后删除原来的jar,将处理后的文件命名为原来的文件。
- 如果存在这个preDex,将这个preDexTask的输入文件进行字节码注入操作,这个Task的输入文件是一系列的jar文件,这些jar是所有的库工程和第三方jar包,此外,还需要将主工程的class文件进行处理。
- 完成了注入字节码操作后,需要对其进行dex操作,也就是最终的patch文件。这个patch文件可以直接被客户端加载并进行热修复。
- 不能注入字节码的类是Application的子类,因为Hack.apk在程序运行之前没有被加载,所以如果Application类中引用了Hack.apk中的Hack.class文件,则会报Class找不到的异常,之后也永远找不到了。所以这个类不能注入字节码,但是需要提前加载初始化方法中动态加载该Hack.apk。
- 发版时的mapping文件以及所有class文件的hash值的文件需要保持下来打patch使用。