【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新,androidtinker
【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代。
Tinker github地址:https://github.com/Tencent/tinker
首先向微信致敬,感谢毫无保留的开源出了这么一款优秀的热更新项目。
因Tinker支持Dex,资源文件及so文件的热更新,本系列将从以下三个方面对Tinker进行源码解析:
Tinker中Dex的热更新也主要分为三个部分,本文也将从这三个方面进行分析:
转载请标明本文来源:http://www.cnblogs.com/yyangblog/p/6249715.html
更多内容欢迎star作者的github:https://github.com/LaurenceYang/article
如果发现本文有什么问题和任何建议,也随时欢迎交流~
一、生成补丁流程
当在命令行里面调用tinkerPatchRelease任务时会调用com.tencent.tinker.build.patch.Runner.tinkerPatch()进行生成补丁生成过程。
1 //gen patch 2 ApkDecoder decoder = new ApkDecoder(config); 3 decoder.onAllPatchesStart(); 4 decoder.patch(config.mOldApkFile, config.mNewApkFile); 5 decoder.onAllPatchesEnd(); 6 7 //gen meta file and version file 8 PatchInfo info = new PatchInfo(config); 9 info.gen(); 10 11 //build patch 12 PatchBuilder builder = new PatchBuilder(config); 13 builder.buildPatch();
ApkDecoder.patch(File oldFile, File newFile)函数中,
会先对manifest文件进行检测,看其是否有更改,如果发现manifest的组件有新增,则抛出异常,因为目前Tinker暂不支持四大组件的新增。
检测通过后解压apk文件,遍历新旧apk,交给ApkFilesVisitor进行处理。
1 //check manifest change first 2 manifestDecoder.patch(oldFile, newFile); 3 4 unzipApkFiles(oldFile, newFile); 5 6 Files.walkFileTree(mNewApkDir.toPath(), new ApkFilesVisitor(config, mNewApkDir.toPath(), mOldApkDir.toPath(), dexPatchDecoder, soPatchDecoder, resPatchDecoder));
ApkFilesVisitor的visitFile函数中,对于dex类型的文件,调用dexDecoder进行patch操作;
对于so类型的文件,使用soDecoder进行patch操作;
对于Res类型文件,使用resDecoder进行操作。
本文中主要是针对dexDecoder进行分析。
1 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 2 3 Path relativePath = newApkPath.relativize(file); 4 5 Path oldPath = oldApkPath.resolve(relativePath); 6 7 File oldFile = null; 8 //is a new file?! 9 if (oldPath.toFile().exists()) { 10 oldFile = oldPath.toFile(); 11 } 12 String patternKey = relativePath.toString().replace("\\", "/"); 13 14 if (Utils.checkFileInPattern(config.mDexFilePattern, patternKey)) { 15 //also treat duplicate file as unchanged 16 if (Utils.checkFileInPattern(config.mResFilePattern, patternKey) && oldFile != null) { 17 resDuplicateFiles.add(oldFile); 18 } 19 20 try { 21 dexDecoder.patch(oldFile, file.toFile()); 22 } catch (Exception e) { 23 // e.printStackTrace(); 24 throw new RuntimeException(e); 25 } 26 return FileVisitResult.CONTINUE; 27 } 28 if (Utils.checkFileInPattern(config.mSoFilePattern, patternKey)) { 29 //also treat duplicate file as unchanged 30 if (Utils.checkFileInPattern(config.mResFilePattern, patternKey) && oldFile != null) { 31 resDuplicateFiles.add(oldFile); 32 } 33 try { 34 soDecoder.patch(oldFile, file.toFile()); 35 } catch (Exception e) { 36 // e.printStackTrace(); 37 throw new RuntimeException(e); 38 } 39 return FileVisitResult.CONTINUE; 40 } 41 if (Utils.checkFileInPattern(config.mResFilePattern, patternKey)) { 42 try { 43 resDecoder.patch(oldFile, file.toFile()); 44 } catch (Exception e) { 45 // e.printStackTrace(); 46 throw new RuntimeException(e); 47 } 48 return FileVisitResult.CONTINUE; 49 } 50 return FileVisitResult.CONTINUE;
DexDiffDecoder.patch(final File oldFile, final File newFile)
首先检测输入的dex文件中是否有不允许修改的类被修改了,如loader相关的类是不允许被修改的,这种情况下会抛出异常;
如果dex是新增的,直接将该dex拷贝到结果文件;
如果dex是修改的,收集增加和删除的class。oldAndNewDexFilePairList将新旧dex对应关系保存起来,用于后面的分析。
1 excludedClassModifiedChecker.checkIfExcludedClassWasModifiedInNewDex(oldFile, newFile); 2 ... 3 //new add file 4 if (oldFile == null || !oldFile.exists() || oldFile.length() == 0) { 5 hasDexChanged = true; 6 if (!config.mUsePreGeneratedPatchDex) { 7 copyNewDexAndLogToDexMeta(newFile, newMd5, dexDiffOut); 8 return true; 9 } 10 } 11 ... 12 // collect current old dex file and corresponding new dex file for further processing.