松岗做网站价格,新网站百度收录要几天,微信门户网站开发,石碣镇做网站*本篇文章已授权微信公众号 guolin_blog #xff08;郭霖#xff09;独家发布
热修复技术自从QQ空间团队搞出来之后便渐渐趋于成熟。
我们这个系列主要介绍如何一步步手动实现基本的热修复功能#xff0c;无需使用第三方框架。
在开始学习之前#xff0c;需要对基本的热…*本篇文章已授权微信公众号 guolin_blog 郭霖独家发布
热修复技术自从QQ空间团队搞出来之后便渐渐趋于成熟。
我们这个系列主要介绍如何一步步手动实现基本的热修复功能无需使用第三方框架。
在开始学习之前需要对基本的热修复技术有些了解以下文章可以帮助到你
安卓App热补丁动态修复技术介绍【腾讯Bugly干货分享】Android Patch 方案与持续交付Android dex分包方案dodola/HotFix 本节课程主要分为3块
1.一步步手动实现热修复(一)-dex文件的生成与加载2.一步步手动实现热修复(二)-类的加载机制简要介绍3.一步步手动实现热修复(三)-Class文件的替换
本节示例所用到的任何资源都已开源项目中包含工程中所用到代码、示例图片、说明文档。项目地址为 https://code.csdn.net/u011064099/sahadevhotfix/tree/master
dex文件的生成与加载
我们在这部分主要做的流程有
1.编写基本的Java文件并编译为.class文件。2.将.class文件转为.dex文件。3.将转好的dex文件放入创建好的Android工程内并在启动时将其写入本地。4.加载解压后的.dex文件中的类并调用其方法进行测试。 Note: 在阅读本节之前最好先了解一下类加载器的双亲委派原则、DexClassLoader的使用以及反射的知识点。 编写基本的Java文件并编译为.class文件
首先我们在一个工程目录下开始创建并编写我们的Java文件你可能会选择各种IDE来做这件事但我在这里劝你不要这么做因为有坑在等你。等把基本流程搞清楚可以再选择更进阶的方法。这里我们可以选择文本编辑器比如EditPlus来对Java文件进行编辑。
新建一个Java文件并命名为ClassStudent.java并在java文件内键入以下代码
public class ClassStudent {private String name;public ClassStudent() {}public void setName(String name) {this.name name;}public String getName(){return this.name .Mr; }
} Note: 这里要注意不要对类添加包名因为在后期对class文件处理时会遇到问题具体问题会稍后说明。上面的getName方法在返回时对this.name属性添加了一段字符串这里请注意后面会用到。 在文件创建好之后对Java文件进行编译
将.class文件转为.dex文件
好现在我们使用class文件生成对应的dex文件。生成dex文件所需要的工具为dxdx工具位于sdk的build-tools文件夹内如下图所示 Tips: 为了方便使用建议将dx的路径添加到环境变量中。如果对dx工具不熟悉的可以在终端中输入dx –help以获取帮助。 dx工具的基本用法是
dx --dex [--outputfile] [file.class | file.{zip,jar,apk} | directory] Tips: 刚开始自己摸索的时候就没有仔细看命令导致后面两个参数的顺序颠倒了搞出了一些让人疑惑难解的问题最后又不得不去找dx工具的源码调试最后才发现自己的问题在哪。如果有对dx工具感兴趣的可以对dx的包进行反编译或者获取dx的相关源代码进行了解。dx.lib文件位于dx.bat的下级目录lib文件夹中可以使用JD-GUI工具对其进行查看或导出。如果需要获取源代码的请使用以下命令进行克隆 git clone https://android.googlesource.com/platform/dalvik 我们使用以下命令生成dex文件
dx --dex --outputuser.dex ClassStudent.class
这里我为了防止出错提前在当前目录下新建好了user.dex文件。上述命令依赖编译.class文件的JDK版本如果使用的是JDK8编译的class会提示以下问题
PARSE ERROR:
unsupported class file version 52.0
...while parsing ClassStudent.class
1 error; aborting
这里的52.0意味着class文件不被支持需要使用JDK8以下的版本进行编译但是dx所需的环境还是需要为JDK8的这里我编译class文件使用的是JDK7,请注意。
上面我们提到了为什么先不要在ClassStudent中使用包名因为在执行dx的时候会报以下异常这是因为以下第二项条件没有通过该代码位于com.android.dx.cf.direct.DirectClassFile文件内 String thisClassName thisClass.getClassType().getClassName();if(!(filePath.endsWith(.class) filePath.startsWith(thisClassName) (filePath.length()(thisClassName.length()6)))){throw new ParseException(class name ( thisClassName ) does not match path ( filePath ));}
运行截图如下所示
好了到此为止我们的目录应该如下
写入dex到本地磁盘
接下来将生成好的user.dex文件放入Android工程的res\raw文件夹下
在系统启动时将其写入到磁盘这里不再贴出具体的写入代码项目的MainActivity中包含了此部分代码。
加载dex中的类并测试
在写入完毕之后使用DexClassLoader对其进行加载。DexClassLoader的构造方法需要4个参数这里对这4个参数进行简要说明
String dexPath:dex文件的绝对路径。在这里我将其放入了应用的cache文件夹下。String optimizedDirectory:优化后的dex文件存放路径。DexClassLoader在构造完毕之后会对原有的dex文件优化并生成一个新的dex文件在这里我选择的是…/cache/optimizedDirectory/目录。此外API文档对该目录有严格的说明Do not cache optimized classes on external storage.出于安全考虑请不要将优化后的dex文件放入外部存储器中。String libraryPath:dex文件所需要的库文件路径。这里没有依赖使用空字符串代替。ClassLoader parent:双亲委派原则中提到的父类加载器。这里我们使用默认的加载器通过getClassLoader()方法获得。
在解释完毕DexClassLoader的构造参数之后我们开始对刚刚的dex文件进行加载
DexClassLoader dexClassLoader new DexClassLoader(apkPath, file.getParent() /optimizedDirectory/, , classLoader);
接来下开始load我们刚刚写入在dex文件中的ClassStudent类
Class? aClass dexClassLoader.loadClass(ClassStudent);
然后我们对其进行初始化并调用相关的get/set方法对其进行验证在这里我传给ClassStudent对象一个字符串然后调用它的get方法获取在方法内合并后的字符串 Object instance aClass.newInstance();Method method aClass.getMethod(setName, String.class);method.invoke(instance, Sahadev);Method getNameMethod aClass.getMethod(getName);Object invoke getNameMethod.invoke(instance););
最后我们实现的代码可能是这样的 /*** 加载指定路径的dex** param apkPath*/private void loadClass(String apkPath) {ClassLoader classLoader getClassLoader();File file new File(apkPath);try {DexClassLoader dexClassLoader new DexClassLoader(apkPath, file.getParent() /optimizedDirectory/, , classLoader);Class? aClass dexClassLoader.loadClass(ClassStudent);mLog.i(TAG, ClassStudent aClass);Object instance aClass.newInstance();Method method aClass.getMethod(setName, String.class);method.invoke(instance, Sahadev);Method getNameMethod aClass.getMethod(getName);Object invoke getNameMethod.invoke(instance);mLog.i(TAG, invoke result invoke);} catch (Exception e) {e.printStackTrace();}}
最后附上我们的运行截图
如果在实现过程中遇到问题的请在下方留言。