【原】Android熱更新開源項目Tinker源碼解析系列之一:Dex熱更新

来源:http://www.cnblogs.com/yyangblog/archive/2017/01/05/6249715.html
-Advertisement-
Play Games

【原】Android熱更新開源項目Tinker源碼解析系列之一:Dex熱更新 Tinker是微信的第一個開源項目,主要用於安卓應用bug的熱修複和功能的迭代。 Tinker github地址:https://github.com/Tencent/tinker 首先向微信致敬,感謝毫無保留的開源出了這 ...


 

【原】Android熱更新開源項目Tinker源碼解析系列之一:Dex熱更新

 

Tinker是微信的第一個開源項目,主要用於安卓應用bug的熱修複和功能的迭代。

Tinker github地址:https://github.com/Tencent/tinker

首先向微信致敬,感謝毫無保留的開源出了這麼一款優秀的熱更新項目。

因Tinker支持Dex,資源文件及so文件的熱更新,本系列將從以下三個方面對Tinker進行源碼解析:

  1. Android熱更新開源項目Tinker源碼解析系列之一:Dex熱更新
  2. Android熱更新開源項目Tinker源碼解析系列之二:資源熱更新
  3. Android熱更新開源項目Tinker源碼解析系類之三:so熱更新

 

Tinker中Dex的熱更新也主要分為三個部分,本文也將從這三個方面進行分析:

  1. 生成補丁流程
  2. 補丁包下發成功後合成全量Dex流程
  3. 生成全量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.
13 oldAndNewDexFilePairList.add(new AbstractMap.SimpleEntry<>(oldFile, newFile));

 

UniqueDexDiffDecoder.patch中將新的dex文件加入到addedDexFiles。

 1 public boolean patch(File oldFile, File newFile) throws IOException, TinkerPatchException {
 2     boolean added = super.patch(oldFile, newFile);
 3     if (added) {
 4         String name = newFile.getName();
 5         if (addedDexFiles.contains(name)) {
 6             throw new TinkerPatchException("illegal dex name, dex name should be unique, dex:" + name);
 7         } else {
 8             addedDexFiles.add(name);
 9         }
10     }
11     return added;
12 }

 

在patch完成後,會調用generatePatchInfoFile生成補丁文件。
DexFiffDecoder.generatePatchInfoFile中首先遍歷oldAndNewDexFilePairList,取出新舊文件對。

判斷新舊文件的MD5是否相等,不相等,說明有變化,會根據新舊文件創建DexPatchGenerator,

DexPatchGenerator構造函數中包含了15個Dex區域的比較演算法:

  • StringDataSectionDiffAlgorithm
  • TypeIdSectionDiffAlgorithm
  • ProtoIdSectionDiffAlgorithm
  • FieldIdSectionDiffAlgorithm
  • MethodIdSectionDiffAlgorithm
  • ClassDefSectionDiffAlgorithm
  • TypeListSectionDiffAlgorithm
  • AnnotationSetRefListSectionDiffAlgorithm
  • AnnotationSetSectionDiffAlgorithm
  • ClassDataSectionDiffAlgorithm
  • CodeSectionDiffAlgorithm
  • DebugInfoItemSectionDiffAlgorithm
  • AnnotationSectionDiffAlgorithm
  • StaticValueSectionDiffAlgorithm
  • AnnotationsDirectorySectionDiffAlgorithm

 

DexDiffDecoder.executeAndSaveTo(OutputStream out) 這個函數裡面會根據上面的15個演算法對dex的各個區域進行比較,最後生成dex文件的差異,

這是整個dex diff演算法的核心以StringDataSectionDiffAlgorithm為例,演算法流程如下:

--------------------------------------------

獲取oldDex中StringData區域的Item,併進行排序
獲取newDex中StringData區域的Item,併進行排序
然後對ITEM依次比較
<0
 說明從老的dex中刪除了該String,patchOperationList中添加Del操作
\>0
 說明添加了該String,patchOperationList添加add操作
=0
 說明都有該String, 記錄oldIndexToNewIndexMap,oldOffsetToNewOffsetMap
old item已到結尾
 剩下的item說明都是新增項,patchOperationList添加add操作
new item已到結尾
 剩下的item說明都是刪除項,patchOperationList添加del操作
最後對對patchOperationList進行優化(
{OP_DEL idx} followed by {OP_ADD the_same_idx newItem} will be replaced by {OP_REPLACE idx newItem})

--------------------------------------------

Dexdiff得到的最終生成產物就是針對原dex的一個操作序列。
關於DexDiff演算法,更加詳細的介紹可以參考https://www.zybuluo.com/dodola/note/554061,演算法名曰二路歸併。

 

對每個區域比較後會將比較的結果寫入文件中,文件格式寫在DexDataBuffer中

 1  private void writeResultToStream(OutputStream os) throws IOException {
 2     DexDataBuffer buffer = new DexDataBuffer();
 3     buffer.write(DexPatchFile.MAGIC);
 4     buffer.writeShort(DexPatchFile.CURRENT_VERSION);
 5     buffer.writeInt(this.patchedDexSize);
 6     // we will return here to write firstChunkOffset later.
 7     int posOfFirstChunkOffsetField = buffer.position();
 8     buffer.writeInt(0);
 9     buffer.writeInt(this.patchedStringIdsOffset);
10     buffer.writeInt(this.patchedTypeIdsOffset);
11     buffer.writeInt(this.patchedProtoIdsOffset);
12     buffer.writeInt(this.patchedFieldIdsOffset);
13     buffer.writeInt(this.patchedMethodIdsOffset);
14     buffer.writeInt(this.patchedClassDefsOffset);
15     buffer.writeInt(this.patchedMapListOffset);
16     buffer.writeInt(this.patchedTypeListsOffset);
17     buffer.writeInt(this.patchedAnnotationSetRefListItemsOffset);
18     buffer.writeInt(this.patchedAnnotationSetItemsOffset);
19     buffer.writeInt(this.patchedClassDataItemsOffset);
20     buffer.writeInt(this.patchedCodeItemsOffset);
21     buffer.writeInt(this.patchedStringDataItemsOffset);
22     buffer.writeInt(this.patchedDebugInfoItemsOffset);
23     buffer.writeInt(this.patchedAnnotationItemsOffset);
24     buffer.writeInt(this.patchedEncodedArrayItemsOffset);
25     buffer.writeInt(this.patchedAnnotationsDirectoryItemsOffset);
26     buffer.write(this.oldDex.computeSignature(false));
27     int firstChunkOffset = buffer.position();
28     buffer.position(posOfFirstChunkOffsetField);
29     buffer.writeInt(firstChunkOffset);
30     buffer.position(firstChunkOffset);
31 
32     writePatchOperations(buffer, this.stringDataSectionDiffAlg.getPatchOperationList());
33     writePatchOperations(buffer, this.typeIdSectionDiffAlg.getPatchOperationList());
34     writePatchOperations(buffer, this.typeListSectionDiffAlg.getPatchOperationList());
35     writePatchOperations(buffer, this.protoIdSectionDiffAlg.getPatchOperationList());
36     writePatchOperations(buffer, this.fieldIdSectionDiffAlg.getPatchOperationList());
37     writePatchOperations(buffer, this.methodIdSectionDiffAlg.getPatchOperationList());
38     writePatchOperations(buffer, this.annotationSectionDiffAlg.getPatchOperationList());
39     writePatchOperations(buffer, this.annotationSetSectionDiffAlg.getPatchOperationList());
40     writePatchOperations(buffer, this.annotationSetRefListSectionDiffAlg.getPatchOperationList());
41     writePatchOperations(buffer, this.annotationsDirectorySectionDiffAlg.getPatchOperationList());
42     writePatchOperations(buffer, this.debugInfoSectionDiffAlg.getPatchOperationList());
43     writePatchOperations(buffer, this.codeSectionDiffAlg.getPatchOperationList());
44     writePatchOperations(buffer, this.classDataSectionDiffAlg.getPatchOperationList());
45     writePatchOperations(buffer, this.encodedArraySectionDiffAlg.getPatchOperationList());
46     writePatchOperations(buffer, this.classDefSectionDiffAlg.getPatchOperationList());
47 
48     byte[] bufferData = buffer.array();
49     os.write(bufferData);
50     os.flush();
51 }

 

生成的文件以dex結尾,但需要註意的是,它不是真正的dex文件,其格式可參考DexDataBuffer類。

 

二、補丁包下發成功後合成全量Dex流程

當app收到伺服器下發的補丁後,會觸發DefaultPatchListener.onPatchReceived事件,

調用TinkerPatchService.runPatchService啟動patch進程進行補丁patch工作。

UpgradePatch.tryPatch()中會首先檢查補丁的合法性,簽名,以及是否安裝過補丁,檢查通過後會嘗試dex,so以及res文件的patch。

本文中主要分析DexDiffPatchInternal.tryRecoverDexFiles,討論dex的patch過程。

1 DexDiffPatchInternal.tryRecoverDexFiles
2 BsDiffPatchInternal.tryRecoverLibraryFiles
3 ResDiffPatchInternal.tryRecoverResourceFiles
4 rewritePatchInfoFileWithLock

 

tryRecoverDexFiles調用DexDiffPatchInternal.patchDexFile,

最終通過DexPatchApplier.executeAndSaveTo進行執行及生產全量dex。

 1 private static void patchDexFile(
 2         ZipFile baseApk, ZipFile patchPkg, ZipEntry oldDexEntry, ZipEntry patchFileEntry,
 3         ShareDexDiffPatchInfo patchInfo,  File patchedDexFile) throws IOException {
 4     InputStream oldDexStream = null;
 5     InputStream patchFileStream = null;
 6     try {
 7         oldDexStream = baseApk.getInputStream(oldDexEntry);
 8         patchFileStream = (patchFileEntry != null ? patchPkg.getInputStream(patchFileEntry) : null);
 9 
10         final boolean isRawDexFile = SharePatchFileUtil.isRawDexFile(patchInfo.rawName);
11         if (!isRawDexFile || patchInfo.isJarMode) {
12             ZipOutputStream zos = null;
13             try {
14                 zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(patchedDexFile)));
15                 zos.putNextEntry(new ZipEntry(ShareConstants.DEX_IN_JAR));
16                 // Old dex is not a raw dex file.
17                 if (!isRawDexFile) {
18                     ZipInputStream zis = null;
19                     try {
20                         zis = new ZipInputStream(oldDexStream);
21                         ZipEntry entry;
22                         while ((entry = zis.getNextEntry()) != null) {
23                             if (ShareConstants.DEX_IN_JAR.equals(entry.getName())) break;
24                         }
25                         if (entry == null) {
26                             throw new TinkerRuntimeException("can't recognize zip dex format file:" + patchedDexFile.getAbsolutePath());
27                         }
28                         new DexPatchApplier(zis, (int) entry.getSize(), patchFileStream).executeAndSaveTo(zos);
29                     } finally {
30                         SharePatchFileUtil.closeQuietly(zis);
31                     }
32                 } else {
33                     new DexPatchApplier(oldDexStream, (int) oldDexEntry.getSize(), patchFileStream).executeAndSaveTo(zos);
34                 }
35                 zos.closeEntry();
36             } finally {
37                 SharePatchFileUtil.closeQuietly(zos);
38             }
39         } else {
40             new DexPatchApplier(oldDexStream, (int) oldDexEntry.getSize(), patchFileStream).executeAndSaveTo(patchedDexFile);
41         }
42     } finally {
43         SharePatchFileUtil.closeQuietly(oldDexStream);
44         SharePatchFileUtil.closeQuietly(patchFileStream);
45     }
46 }

 

DexPatchApplier.executeAndSaveTo(OutputStream out)中會對15個dex區域進行patch操作,

針對old dex和patch dex進行合併,生成全量dex文件。

  1 public void executeAndSaveTo(OutputStream out) throws IOException {
  2     // Before executing, we should check if this patch can be applied to
  3     // old dex we passed in.
  4     // 首先old apk的簽名和patchfile所攜帶的old apk簽名是否一致,不一致則拋出異常
  5     byte[] oldDexSign = this.oldDex.computeSignature(false);
  6     if (oldDexSign == null) {
  7         throw new IOException("failed to compute old dex's signature.");
  8     }
  9 
 10     if (this.patchFile != null) {
 11         byte[] oldDexSignInPatchFile = this.patchFile.getOldDexSignature();
 12         if (CompareUtils.uArrCompare(oldDexSign, oldDexSignInPatchFile) != 0) {
 13             throw new IOException(
 14                     String.format(
 15                             "old dex signature mismatch! expected: %s, actual: %s",
 16                             Arrays.toString(oldDexSign),
 17                             Arrays.toString(oldDexSignInPatchFile)
 18                     )
 19             );
 20         }
 21     }
 22 
 23     String oldDexSignStr = Hex.toHexString(oldDexSign);
 24 
 25     // Firstly, set sections' offset after patched, sort according to their offset so that
 26     // the dex lib of aosp can calculate section size.
 27     // patchedDex是最終合成的dex,首先設定各個區域的偏移量
 28     TableOfContents patchedToc = this.patchedDex.getTableOfContents();
 29 
 30     patchedToc.header.off = 0;
 31     patchedToc.header.size = 1;
 32     patchedToc.mapList.size = 1;
 33 
 34     if (extraInfoFile == null || !extraInfoFile.isAffectedOldDex(this.oldDexSignStr)) {
 35         patchedToc.stringIds.off
 36                 = this.patchFile.getPatchedStringIdSectionOffset();
 37         patchedToc.typeIds.off
 38                 = this.patchFile.getPatchedTypeIdSectionOffset();
 39         patchedToc.typeLists.off
 40                 = this.patchFile.getPatchedTypeListSectionOffset();
 41         patchedToc.protoIds.off
 42                 = this.patchFile.getPatchedProtoIdSectionOffset();
 43         patchedToc.fieldIds.off
 44                 = this.patchFile.getPatchedFieldIdSectionOffset();
 45         patchedToc.methodIds.off
 46                 = this.patchFile.getPatchedMethodIdSectionOffset();
 47         patchedToc.classDefs.off
 48                 = this.patchFile.getPatchedClassDefSectionOffset();
 49         patchedToc.mapList.off
 50                 = this.patchFile.getPatchedMapListSectionOffset();
 51         patchedToc.stringDatas.off
 52                 = this.patchFile.getPatchedStringDataSectionOffset();
 53         patchedToc.annotations.off
 54                 = this.patchFile.getPatchedAnnotationSectionOffset();
 55         patchedToc.annotationSets.off
 56                 = this.patchFile.getPatchedAnnotationSetSectionOffset();
 57         patchedToc.annotationSetRefLists.off
 58                 = this.patchFile.getPatchedAnnotationSetRefListSectionOffset();
 59         patchedToc.annotationsDirectories.off
 60                 = this.patchFile.getPatchedAnnotationsDirectorySectionOffset();
 61         patchedToc.encodedArrays.off
 62                 = this.patchFile.getPatchedEncodedArraySectionOffset();
 63         patchedToc.debugInfos.off
 64                 = this.patchFile.getPatchedDebugInfoSectionOffset();
 65         patchedToc.codes.off
 66                 = this.patchFile.getPatchedCodeSectionOffset();
 67         patchedToc.classDatas.off
 68                 = this.patchFile.getPatchedClassDataSectionOffset();
 69         patchedToc.fileSize
 70                 = this.patchFile.getPatchedDexSize();
 71     } else {
 72        ...
 73     }
 74 
 75     Arrays.sort(patchedToc.sections);
 76 
 77     patchedToc.computeSizesFromOffsets();
 78 
 79     // Secondly, run patch algorithms according to sections' dependencies.
 80     // 對每個區域進行patch操作
 81     this.stringDataSectionPatchAlg = new StringDataSectionPatchAlgorithm(
 82             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
 83             patchedToSmallPatchedIndexMap, extraInfoFile
 84     );
 85     this.typeIdSectionPatchAlg = new TypeIdSectionPatchAlgorithm(
 86             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
 87             patchedToSmallPatchedIndexMap, extraInfoFile
 88     );
 89     this.protoIdSectionPatchAlg = new ProtoIdSectionPatchAlgorithm(
 90             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
 91             patchedToSmallPatchedIndexMap, extraInfoFile
 92     );
 93     this.fieldIdSectionPatchAlg = new FieldIdSectionPatchAlgorithm(
 94             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
 95             patchedToSmallPatchedIndexMap, extraInfoFile
 96     );
 97     this.methodIdSectionPatchAlg = new MethodIdSectionPatchAlgorithm(
 98             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
 99             patchedToSmallPatchedIndexMap, extraInfoFile
100     );
101     this.classDefSectionPatchAlg = new ClassDefSectionPatchAlgorithm(
102             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
103             patchedToSmallPatchedIndexMap, extraInfoFile
104     );
105     this.typeListSectionPatchAlg = new TypeListSectionPatchAlgorithm(
106             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
107             patchedToSmallPatchedIndexMap, extraInfoFile
108     );
109     this.annotationSetRefListSectionPatchAlg = new AnnotationSetRefListSectionPatchAlgorithm(
110             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
111             patchedToSmallPatchedIndexMap, extraInfoFile
112     );
113     this.annotationSetSectionPatchAlg = new AnnotationSetSectionPatchAlgorithm(
114             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
115             patchedToSmallPatchedIndexMap, extraInfoFile
116     );
117     this.classDataSectionPatchAlg = new ClassDataSectionPatchAlgorithm(
118             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
119             patchedToSmallPatchedIndexMap, extraInfoFile
120     );
121     this.codeSectionPatchAlg = new CodeSectionPatchAlgorithm(
122             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
123             patchedToSmallPatchedIndexMap, extraInfoFile
124     );
125     this.debugInfoSectionPatchAlg = new DebugInfoItemSectionPatchAlgorithm(
126             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
127             patchedToSmallPatchedIndexMap, extraInfoFile
128     );
129     this.annotationSectionPatchAlg = new AnnotationSectionPatchAlgorithm(
130             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
131             patchedToSmallPatchedIndexMap, extraInfoFile
132     );
133     this.encodedArraySectionPatchAlg = new StaticValueSectionPatchAlgorithm(
134             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
135             patchedToSmallPatchedIndexMap, extraInfoFile
136     );
137     this.annotationsDirectorySectionPatchAlg = new AnnotationsDirectorySectionPatchAlgorithm(
138             patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
139             patchedToSmallPatchedIndexMap, extraInfoFile
140     );
141 
142     this.stringDataSectionPatchAlg.execute();
143     this.typeIdSectionPatchAlg.execute();
144     this.typeListSectionPatchAlg.execute();
145     this.protoIdSectionPatchAlg.execute();
146     this.fieldIdSectionPatchAlg.execute();
147     this.methodIdSectionPatchAlg.execute();
148     Runtime.getRuntime().gc();
149     this.annotationSectionPatchAlg.execute();
150     this.annotationSetSectionPatchAlg.execute();
151     this.annotationSetRefListSectionPatchAlg.execute();
152     this.annotationsDirectorySectionPatchAlg.execute();
153     Runtime.getRuntime().gc();
154     this.debugInfoSectionPatchAlg.execute();
155     this.codeSectionPatchAlg.execute();
156     Runtime.getRuntime().gc();
157     this.classDataSectionPatchAlg.execute();
158     this.encodedArraySectionPatchAlg.execute();
159     this.classDefSectionPatchAlg.execute();
160     Runtime.getRuntime().gc();
161 
162     // Thirdly, write header, mapList. Calculate and write patched dex's sign and checksum.
163     Dex.Section headerOut = this.patchedDex.openSection(patchedToc.header.off);
164     patchedToc.writeHeader(headerOut);
165 
166     Dex.Section mapListOut = this.patchedDex.openSection(patchedToc.mapList.off);
167     patchedToc.writeMap(mapListOut);
168 
169     this.patchedDex.writeHashes();
170 
171     // Finally, write patched dex to file.
172     this.patchedDex.writeTo(out);

 

每個區域的合併演算法採用二路歸併,在old dex的基礎上對元素進行刪除,增加,替換操作。

這裡的演算法和生成補丁的DexDiff是一個逆向的過程。

 1 private void doFullPatch(
 2         Dex.Section oldSection,
 3         int oldItemCount,
 4         int[] deletedIndices,
 5
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.如果可以重置模擬器 首先試試重置模擬器 2.如果不能重置,可以選擇使用如下命令殺死模擬器服務: killall -9 com.apple.CoreSimulator.CoreSimulatorService //殺死模擬器服務 3.使用如下命令刪除模擬器目錄下是所有文件 rm -rf ~/Lib ...
  • 一、使用Raw文件夾下的資料庫文件 在使用GreenDao框架時,資料庫和數據表都是根據生成的框架代碼來自動創建的,從生成的DaoMaster中的OpenHelper類可以看出: 對應的createAllTables函數代碼: 再接著往下看: 從以上的代碼可以看出GreenDao在第一次使用的時候會 ...
  • 今天在看到App Store 上架過程中,蘋果公司反饋的拒絕原因發現了這麼一個問題: Legal - 5.1.5 Your app uses background location services but does not clarify the purpose of its use in the ...
  • 上一篇文章介紹了Dex文件的熱更新流程,本文將會分析Tinker中對資源文件的熱更新流程。 同Dex,資源文件的熱更新同樣包括三個部分:資源補丁生成,資源補丁合成及資源補丁載入。 本系列將從以下三個方面對Tinker進行源碼解析: 轉載請標明本文來源:http://www.cnblogs.com/y ...
  • 之前在博文中為了更好的給大家演示APP的實現效果,本人瞭解學習了幾種給手機錄屏的方法,今天就給大家介紹兩種我個人用的比較舒服的兩種方法: (1)配置adb環境後,使用cmd命令將手機界面操作演示存為視頻文件 (2)使用Google瀏覽器(Google Chrome)提供的擴展程式Vysor將手機界面 ...
  • RecylerView介紹 RecylerView是support v7包中的新組件,是一個強大的滑動組件,與經典的ListView相比,同樣擁有item回收復用的功能,這一點從它的名字recylerview即回收view也可以看出。官方對於它的介紹則是:RecyclerView 是 ListVie ...
  • 由[OpenDigg](http://www.opendigg.com/) 出品的iOS開源項目周報第四期來啦。我們的iOS開源周報集合了OpenDigg一周來新收錄的優質的iOS開發方面的開源項目,方便iOS開發人員便捷的找到自己需要的項目工具等。 ...
  • 轉載自: 來自AR學院(www.arvrschool.com),原文地址為:http://www.arvrschool.com/index.php?c=post&a=modify&tid=687&pid=0 謝謝合作! 官方交流群:129340649 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...