wordpress建站模版,seo推广任务小结,网站建设教学视频,wordpress导航改哪个php文件夹*本篇文章已授权微信公众号 guolin_blog #xff08;郭霖#xff09;独家发布
本节课程主要分为3块#xff1a;
1.一步步手动实现热修复(一)-dex文件的生成与加载2.一步步手动实现热修复(二)-类的加载机制简要介绍3.一步步手动实现热修复(三)-Class文件的替换
本节示例所…*本篇文章已授权微信公众号 guolin_blog 郭霖独家发布
本节课程主要分为3块
1.一步步手动实现热修复(一)-dex文件的生成与加载2.一步步手动实现热修复(二)-类的加载机制简要介绍3.一步步手动实现热修复(三)-Class文件的替换
本节示例所用到的任何资源都已开源项目中包含工程中所用到代码、示例图片、说明文档。项目地址为 https://code.csdn.net/u011064099/sahadevhotfix/tree/master 在上一节了解了基本的类加载原理之后我们这一节开始对工程内部的类实行替换。 Tips: 本章主要依赖文章http://blog.csdn.net/vurtne_ye/article/details/39666381中的未实现代码实现实现思路也源自该文章在阅读本文之前可以先行了解。 这一节我们主要实现的流程有
在工程内创建相同的ClassStudent类但在调用getName()方法返回字符串时会稍有区别用于结果验证使用DexClassLoader加载外部的user.dex将DexClassLoader中的dexElements放在PathClassLoader的dexElements之前验证替换结果
创建工程内的ClassStudent
我们在第一节中演示了如何加载外部的Class为了起到热修复效果那么我们需要在工程内有一个被替换的类被替换的ClassStudent类内容如下
package com.sahadev.bean;/*** Created by shangbin on 2016/11/24.* Email: sahadevfoxmail.com*/public class ClassStudent {private String name;public ClassStudent() {}public void setName(String name) {this.name name;}public String getName(){return this.name .Miss;}}
外部的ClassStudent类的内容如下
package com.sahadev.bean;/*** Created by shangbin on 2016/11/24.* Email: sahadevfoxmail.com*/public class ClassStudent {private String name;public ClassStudent() {}public void setName(String name) {this.name name;}public String getName(){return this.name .Mr; }
}
这两个类除了在getName()方法返回之处有差别之外其它地方一模一样不过这足可以让我们说明情况。 我们这里要实现的目的 我们默认调用getName()方法返回的是“xxxx.Miss”如果热修复成功那么再使用该方法的话返回的则会是“xxxx.Mr”。 对含有包名的类再次编译
因为第一节中专门声明了不可以对类声明包名但是这样在Android工程中无法引用到该类所以把不能声明包名的问题解决了一下。
不能声明包名的主要原因是在编译Java文件时没有正确的使用命令。对含有包名的Java文件应当使用以下命令
javac -d ./ ClassStudent.java
经过上面命令编译后的.class文件便可以顺利通过dx工具的转换。
我们还是按照第一节的步骤将转换后的user.dex文件放入工程中并写入本地磁盘以便稍后使用。 Dex转换注意: 很多同学走到这一步时遇到了问题这是因为第一章介绍的时候并没有带包名所以在这里我们所使用的命令的最后一个参数应当携带包的相对路径例如 dx –dex –outputuser.dex .\com\sahadev\bean\ClassStudent.class 替换工程内的类文件
在开始之前还是再回顾一下实现思路类在使用之前必须要经过加载器的加载才能够使用在加载类时会调用自身的findClass()方法进行查找。然而在Android中类的查找使用的是BaseDexClassLoaderBaseDexClassLoader对findClass()方法进行了重写 Overrideprotected Class? findClass(String name) throws ClassNotFoundException {Class clazz pathList.findClass(name);if (clazz null) {throw new ClassNotFoundException(name);}return clazz;}
pathList是类DexPathList的实例这里pathList.findClass的实现如下 public Class findClass(String name) {for (Element element : dexElements) {DexFile dex element.dexFile;if (dex ! null) {Class clazz dex.loadClassBinaryName(name, definingContext);if (clazz ! null) {return clazz;}}}return null;}
由此我们可以得知类的查找是通过遍历dexElements来进行查找的。所以为了实现替换效果我们需要将DexClassLoader中的Element对象放到dexElements数组的第0个位置这样才能在BaseDexClassLoader查找类时先找到DexClassLoader所用的user.dex中的类。 Tips: 如果对上面这段内容看不懂的没关系可以移步到本系列课程的第二节了解一下类加载的具体流程。 类的加载是从上而下加载的所以就算是DexClassLoader加载了外部的类但是在系统使用类的时候还是会先在ClassLoader中查找如果找不到则会在BaseDexClassLoader中查找如果再找不到就会进入PathClassLoader中查找最后才会使用DexClassLoader进行查找所以按照这个流程外部类是无法正常发挥作用的。所以我们的目的就是在查找工程内的类之前先让加载器去外部的dex中查找。
好了再次梳理了思路之后我们接下来对思路进行实践。
下面的方法是我们主要的注入方法 public String inject(String apkPath) {boolean hasBaseDexClassLoader true;File file new File(apkPath);try {Class.forName(dalvik.system.BaseDexClassLoader);} catch (ClassNotFoundException e) {hasBaseDexClassLoader false;}if (hasBaseDexClassLoader) {PathClassLoader pathClassLoader (PathClassLoader) getClassLoader();DexClassLoader dexClassLoader new DexClassLoader(apkPath, file.getParent() /optimizedDirectory/, , pathClassLoader);try {Object dexElements combineArray(getDexElements(getPathList(pathClassLoader)), getDexElements(getPathList(dexClassLoader)));Object pathList getPathList(pathClassLoader);setField(pathList, pathList.getClass(), dexElements, dexElements);return SUCCESS;} catch (Throwable e) {e.printStackTrace();return android.util.Log.getStackTraceString(e);}}return SUCCESS;} Tips: 这段代码原封不动采用于http://blog.csdn.net/vurtne_ye/article/details/39666381文章中最后的实现代码但是该文章并没有给出具体的注入细节。我们接下里的过程就是对没有给全的细节进行补充与讲解。 这段代码的核心在于将DexClassLoader中的dexElements与PathClassLoader中的dexElements进行合并然后将合并后的dexElements替换原先的dexElements。最后我们在使用ClassStudent类的时候便可以直接使用外部的ClassStudent而不会再加载默认的ClassStudent类。
首先我们通过classLoader获取各自的pathList对象
public Object getPathList(BaseDexClassLoader classLoader) {Class? extends BaseDexClassLoader aClass classLoader.getClass();Class? superclass aClass.getSuperclass();try {Field pathListField superclass.getDeclaredField(pathList);pathListField.setAccessible(true);Object object pathListField.get(classLoader);return object;} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;
}
在使用以上反射的时候要注意pathList属性属于基类BaseDexClassLoader。所以如果直接获取DexClassLoader或者PathClassLoader的pathList属性的话会得到null。
其次是获取pathList对应的dexElements这里要注意dexElements是个数组对象 public Object getDexElements(Object object) {if (object null)return null;Class? aClass object.getClass();try {Field dexElements aClass.getDeclaredField(dexElements);dexElements.setAccessible(true);return dexElements.get(object);} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}
接下来我们将两个数组对象合并成为一个 public Object combineArray(Object object, Object object2) {Class? aClass Array.get(object, 0).getClass();Object obj Array.newInstance(aClass, 2);Array.set(obj, 0, Array.get(object2, 0));Array.set(obj, 1, Array.get(object, 0));return obj;}
上面这段代码我们根据数组对象的类型创建了一个新的大小为2的新数组并将两个数组的第一个元素取出将代表外部dex的dexElement放在了第0个位置。这样便可以确保在查找类时优先从外部的dex中查找。
最后将原先的dexElements覆盖 public void setField(Object pathList, Class aClass, String fieldName, Object fieldValue) {try {Field declaredField aClass.getDeclaredField(fieldName);declaredField.setAccessible(true);declaredField.set(pathList, fieldValue);} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}
验证替换结果
好我们做完以上的工作之后写一段代码来进行验证 /*** 验证替换类后的效果*/private void demonstrationRawMode() {ClassStudent classStudent new ClassStudent();classStudent.setName(Lavon);mLog.i(TAG, classStudent.getName());}
如果我们没有替换成功的话那么这里默认使用的是内部的ClassStudentgetName()返回的会是Lavon.Miss。 如果我们替换成功的话那么这里默认使用的是外部的ClassStudentgetName()返回的则会是Lavon.Mr。
我们实际运行看下效果
这说明我们已经完成了基本的热修复。有任何疑问欢迎留言。 我建了一个QQ群欢迎对学习有兴趣的同学加入。我们可以一起探讨、深究、掌握那些我们会用到的技术让自己不至于太落伍。