Android热修复实现原理以及方法
2017-03-23 15:06
513 查看
现在主要由两大方法
1.阿里AndFix,主要是采用Ndk实现对方法指针的替换
2.腾讯Tinker
现在主要说的是tinker的实现方法:
一. 首先介绍下两个概念:
public class PathClassLoader extends BaseDexClassLoader {
用来加载已安装应用程序的dex
public class DexClassLoader extends BaseDexClassLoader {
可以加载指定的某个dex文件。(限制:必须要在应用程序的目录下面,所以下载下来需要复制到本目录里)
二. 很明显我们需要使用DexClassLoader来实现需求
上面创建了一个DexPathList
加载类的过程:
ClassLoader通过loadClass来加载需要的类
最后还是调用的DexPathList的findClass
从上面代码看出如果element里面找到了class,for循环就会退出
所以我们只要修改dexElements这个集合就行了,把我们创建的dexElements跟原始的dexElements合并。
dexA:原app的dex文件
dexB:补丁dex文件,可以是多个
1.先把下载下来的dex文件用file形式保存到一个集合中
2.使用反射获取dexA,dexB的classLoader,dexA的classLoader使用PathClassLoader获取,dexB的classLoader由dexClassLoader获取
3.通过classloader获取dexA和dexB的pathList
4.通过pathList获取dexA和dexB的dexElements,并把这两个合并
5.把合并之后的dexElements赋值给dexA的classLoader的pathList中的dexElements
那么具体步骤就已经完成了。
6.怎么才能打出一个dex包?
通过dx.bat这个系统提供的工具就可以
代码地址如下:
1.阿里AndFix,主要是采用Ndk实现对方法指针的替换
2.腾讯Tinker
现在主要说的是tinker的实现方法:
一. 首先介绍下两个概念:
public class PathClassLoader extends BaseDexClassLoader {
用来加载已安装应用程序的dex
public class DexClassLoader extends BaseDexClassLoader {
可以加载指定的某个dex文件。(限制:必须要在应用程序的目录下面,所以下载下来需要复制到本目录里)
二. 很明显我们需要使用DexClassLoader来实现需求
public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) { super(parent); this.originalPath = dexPath; this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory); }
上面创建了一个DexPathList
public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) { …… this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory); } private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory) { ArrayList<Element> elements = new ArrayList<Element>(); for (File file : files) { ZipFile zip = null; DexFile dex = null; String name = file.getName(); if (name.endsWith(DEX_SUFFIX)) { dex = loadDexFile(file, optimizedDirectory); } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX) || name.endsWith(ZIP_SUFFIX)) { zip = new ZipFile(file); } …… if ((zip != null) || (dex != null)) { elements.add(new Element(file, zip, dex)); } } return elements.toArray(new Element[elements.size()]); } private static DexFile loadDexFile(File file, File optimizedDirectory) throws IOException { if (optimizedDirectory == null) { return new DexFile(file); } else { String optimizedPath = optimizedPathFor(file, optimizedDirectory); return DexFile.loadDex(file.getPath(), optimizedPath, 0); } } /** * Converts a dex/jar file path and an output directory to an * output file path for an associated optimized dex file. */ private static String optimizedPathFor(File path, File optimizedDirectory) { String fileName = path.getName(); if (!fileName.endsWith(DEX_SUFFIX)) { int lastDot = fileName.lastIndexOf("."); if (lastDot < 0) { fileName += DEX_SUFFIX; } else { StringBuilder sb = new StringBuilder(lastDot + 4); sb.append(fileName, 0, lastDot); sb.append(DEX_SUFFIX); fileName = sb.toString(); } } File result = new File(optimizedDirectory, fileName); return result.getPath(); }
optimizedDirectory是用来缓存我们需要加载的dex文件的,并创建一个DexFile对象,如果它为null,那么会直接使用dex文件原有的路径来创建DexFile 对象。
加载类的过程:
ClassLoader通过loadClass来加载需要的类
public Class<?> loadClass(String className) throws ClassNotFoundException { return loadClass(className, false); } protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { Class<?> clazz = findLoadedClass(className); if (clazz == null) { ClassNotFoundException suppressed = null; try { clazz = parent.loadClass(className, false); } catch (ClassNotFoundException e) { suppressed = e; } if (clazz == null) { try { clazz = findClass(className); } catch (ClassNotFoundException e) { e.addSuppressed(suppressed); throw e; } } } return clazz; }
最后还是调用的DexPathList的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; }
从上面代码看出如果element里面找到了class,for循环就会退出
所以我们只要修改dexElements这个集合就行了,把我们创建的dexElements跟原始的dexElements合并。
dexA:原app的dex文件
dexB:补丁dex文件,可以是多个
1.先把下载下来的dex文件用file形式保存到一个集合中
//遍历所有的修复的dex File fileDir = context.getDir(MyConstants.DEX_DIR,Context.MODE_PRIVATE); File[] listFiles = fileDir.listFiles(); for(File file:listFiles){ if(file.getName().startsWith("classes")&&file.getName().endsWith(".dex")){ loadedDex.add(file);//存入集合 } }
2.使用反射获取dexA,dexB的classLoader,dexA的classLoader使用PathClassLoader获取,dexB的classLoader由dexClassLoader获取
PathClassLoader pathLoader = (PathClassLoader) appContext.getClassLoader(); for (File dex : loadedDex) { //2.加载指定的修复的dex文件。 DexClassLoader classLoader = new DexClassLoader( dex.getAbsolutePath(),//String dexPath, fopt.getAbsolutePath(),//String optimizedDirectory, null,//String libraryPath, pathLoader//ClassLoader parent ); }
3.通过classloader获取dexA和dexB的pathList
Object dexObj = getPathList(classLoader); Object pathObj = getPathList(pathLoader);
4.通过pathList获取dexA和dexB的dexElements,并把这两个合并
Object mDexElementsList = getDexElements(dexObj); Object pathDexElementsList = getDexElements(pathObj); //合并完成 Object dexElements = combineArray(mDexElementsList,pathDexElementsList);
5.把合并之后的dexElements赋值给dexA的classLoader的pathList中的dexElements
Object mDexElementsList = getDexElements(dexObj); Object pathDexElementsList = getDexElements(pathObj); //合并完成 Object dexElements = combineArray(mDexElementsList,pathDexElementsList);
//重写给PathList里面的lement[] dexElements;赋值
Object pathList = getPathList(pathLoader);
setField(pathList,pathList.getClass(),"dexElements",dexElements);
那么具体步骤就已经完成了。
6.怎么才能打出一个dex包?
通过dx.bat这个系统提供的工具就可以
1.找到MyTestClass.class com\app\build\intermediates\bin\MyTestClass.class 2.配置dx.bat的环境变量 Android\sdk\build-tools\23.0.3\dx.bat 3.命令 dx --dex --output=D:\Users\jiang\Desktop\dex\classes2.dex D:\Users\jiang\Desktop\dex 命令解释: --output=D:\Users\jiang\Desktop\dex\classes2.dex 指定输出路径 D:\Users\jiang\Desktop\dex 最后指定去打包哪个目录下面的class字节文件(注意要包括全路径的文件夹,也可以有多个class)
代码地址如下:
相关文章推荐
- Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复
- Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复
- Android高速截屏之vysor反射截屏原理实现方法以及代码下载
- Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复
- Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复
- Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复
- Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复
- Android 中免 Root 实现 Hook 的 Dexposed 实现原理解析以及如何实现应用的热修复
- Android-Socket的最基础实现以及遇见在2.3可用4.3不可用的解决方法
- SAMBA 的实现原理以及使用方法
- mdev的使用方法和原理以及实现U盘或SD卡的自动挂载
- android中获取root权限的方法以及原理(转)
- C语言中strtok使用方法与原理,以及自实现函数功能
- Android中RadioGroup RadioButton CheckBox多选按钮实现方法以及监听方法
- SAMBA 的实现原理以及使用方法
- Android检测Cursor泄漏的原理以及使用方法(转)
- mdev的使用方法和原理以及实现U盘或SD卡的自动挂载
- Android简单涂鸦以及撤销、重做的实现方法
- 了解MmMapIoSpace以及MmUnmapIoSpace函数的实现原理以及实现方法
- 了解MmMapIoSpace以及MmUnmapIoSpace函数的实现原理以及实现方法