美文网首页
热修复框架 - Tinker DexDiff算法浅析

热修复框架 - Tinker DexDiff算法浅析

作者: Stan_Z | 来源:发表于2020-08-20 11:32 被阅读0次

    代码:tinker 1.9.14.7

    这篇文章主要是学习@鸿洋 @dodola 写的相关文章:
    Android 热修复 Tinker 源码分析之DexDiff / DexPatch
    Tinker

    一、dex文件结构

    用010 Editor 看下dex文件结构

    header 描述了 dex 文件信息,和其他各个区的索引id大小和偏移量,同时可校验文件格式是否正确,文件是否被篡改等。

    image.png

    string_ids 索引字符串。偏移量指向了 string_data 区段的一个字符串

    这里分了10多个区域,包括string、field、method、class等等,每个部分都分别记录起始地址index,偏移量offset,和具体内容,当然也包括一些其他的东西,这里不赘述。

    其他的就不一一看了。

    二、dexDif算法

    首先看看官方写的算法介绍文章:

    首先我们需要将新旧内容排序,排序后由一对指针分别指向old和new的起始值,通过compareTo进行内容比较:

    old < new 添加del标签 old指针++
    old > new 添加add标签 new指针++
    old = new 添加no标签 两个指针同时++

    最终梳理结果:
    保留old del 和new add,另外同一index old del ,new add的改为replece。

    举例:
    old new
    11 foo2 foo3
    12 foo5 foo5
    13 hello dodola hello dodola1

    比较过程:
    old_11 new_11 cmp <0 del
    old_12 new_11 cmp >0 add
    old_12 new_12 cmp =0 no
    old_13 new_13 cmp <0 del

    梳理:
    old_11 del
    new_11 add
    old_13 del

    转为:
    old_11 replace
    old_13 del

    这里用二路递归整理出diff变化内容,并打上标记。

    三、dexdiff代码执行

    DexPatchGenerator dexPatchGenerator
                = new DexPatchGenerator(new File(old.dex), new File(new.dex));
        dexPatchGenerator.executeAndSaveTo(new patch.dex);
    

    先看DexPatchGenerator初始化过程:

    public DexPatchGenerator(Dex oldDex, Dex newDex) {
        this.oldDex = oldDex;
       this.newDex = newDex;
    ...
       this.stringDataSectionDiffAlg = new StringDataSectionDiffAlgorithm(
                oldDex, newDex,
               oldToNewIndexMap,
               oldToPatchedIndexMap,
               newToPatchedIndexMap,
               selfIndexMapForSkip
        );
    ...
    }
    

    这里将新旧dex文件封装为Dex对象。并且对各section初始化对应的算法,这里以StringDataSectionDiffAlgorithm为例:

    class StringDataSectionDiffAlgorithm extends DexSectionDiffAlgorithm<StringData>
    

    每个算法都会执行execute和simulatePatchOperation方法。

    execute:

    public void execute() {
        this.patchOperationList.clear();
       this.adjustedOldIndexedItemsWithOrigOrder = collectSectionItems(this.oldDex, true);
       this.oldItemCount = this.adjustedOldIndexedItemsWithOrigOrder.length;
       AbstractMap.SimpleEntry<Integer, T>[] adjustedOldIndexedItems = new AbstractMap.SimpleEntry[this.oldItemCount];
       System.arraycopy(this.adjustedOldIndexedItemsWithOrigOrder, 0, adjustedOldIndexedItems, 0, this.oldItemCount);
       Arrays.sort(adjustedOldIndexedItems, this.comparatorForItemDiff);
       AbstractMap.SimpleEntry<Integer, T>[] adjustedNewIndexedItems = collectSectionItems(this.newDex, false);
       this.newItemCount = adjustedNewIndexedItems.length;
       Arrays.sort(adjustedNewIndexedItems, this.comparatorForItemDiff);
       int oldCursor = 0;
       int newCursor = 0;
       while (oldCursor < this.oldItemCount || newCursor < this.newItemCount) {
            if (oldCursor >= this.oldItemCount) {
                // rest item are all newItem.
               while (newCursor < this.newItemCount) {
                    AbstractMap.SimpleEntry<Integer, T> newIndexedItem = adjustedNewIndexedItems[newCursor++];
                   this.patchOperationList.add(new PatchOperation<>(PatchOperation.OP_ADD, newIndexedItem.getKey(), newIndexedItem.getValue()));
               }
            } else
           if (newCursor >= newItemCount) {
                // rest item are all oldItem.
               while (oldCursor < oldItemCount) {
                    AbstractMap.SimpleEntry<Integer, T> oldIndexedItem = adjustedOldIndexedItems[oldCursor++];
                   int deletedIndex = oldIndexedItem.getKey();
                   int deletedOffset = getItemOffsetOrIndex(deletedIndex, oldIndexedItem.getValue());
                   this.patchOperationList.add(new PatchOperation<T>(PatchOperation.OP_DEL, deletedIndex));
                   markDeletedIndexOrOffset(this.oldToPatchedIndexMap, deletedIndex, deletedOffset);
               }
            } else {
                AbstractMap.SimpleEntry<Integer, T> oldIndexedItem = adjustedOldIndexedItems[oldCursor];
               AbstractMap.SimpleEntry<Integer, T> newIndexedItem = adjustedNewIndexedItems[newCursor];
               int cmpRes = oldIndexedItem.getValue().compareTo(newIndexedItem.getValue());
               if (cmpRes < 0) {
                    int deletedIndex = oldIndexedItem.getKey();
                   int deletedOffset = getItemOffsetOrIndex(deletedIndex, oldIndexedItem.getValue());
                   this.patchOperationList.add(new PatchOperation<T>(PatchOperation.OP_DEL, deletedIndex));
                   markDeletedIndexOrOffset(this.oldToPatchedIndexMap, deletedIndex, deletedOffset);
                   ++oldCursor;
               } else
               if (cmpRes > 0) {
                    this.patchOperationList.add(new PatchOperation<>(PatchOperation.OP_ADD, newIndexedItem.getKey(), newIndexedItem.getValue()));
                   ++newCursor;
               } else {
                    int oldIndex = oldIndexedItem.getKey();
                   int newIndex = newIndexedItem.getKey();
                   int oldOffset = getItemOffsetOrIndex(oldIndexedItem.getKey(), oldIndexedItem.getValue());
                   int newOffset = getItemOffsetOrIndex(newIndexedItem.getKey(), newIndexedItem.getValue());
                   if (oldIndex != newIndex) {
                        this.oldIndexToNewIndexMap.put(oldIndex, newIndex);
                   }
    
                    if (oldOffset != newOffset) {
                        this.oldOffsetToNewOffsetMap.put(oldOffset, newOffset);
                   }
                    ++oldCursor;
                   ++newCursor;
               }
            }
        }
        // So far all diff works are done. Then we perform some optimize works.
       // detail: {OP_DEL idx} followed by {OP_ADD the_same_idx newItem}
       // will be replaced by {OP_REPLACE idx newItem}
       Collections.sort(this.patchOperationList, comparatorForPatchOperationOpt);
       Iterator<PatchOperation<T>> patchOperationIt = this.patchOperationList.iterator();
       PatchOperation<T> prevPatchOperation = null;
       while (patchOperationIt.hasNext()) {
            PatchOperation<T> patchOperation = patchOperationIt.next();
           if (prevPatchOperation != null
               && prevPatchOperation.op == PatchOperation.OP_DEL
               && patchOperation.op == PatchOperation.OP_ADD
           ) {
                if (prevPatchOperation.index == patchOperation.index) {
                    prevPatchOperation.op = PatchOperation.OP_REPLACE;
                   prevPatchOperation.newItem = patchOperation.newItem;
                   patchOperationIt.remove();
                   prevPatchOperation = null;
               } else {
                    prevPatchOperation = patchOperation;
               }
            } else {
                prevPatchOperation = patchOperation;
           }
        }
        // Finally we record some information for the final calculations.
       patchOperationIt = this.patchOperationList.iterator();
       while (patchOperationIt.hasNext()) {
            PatchOperation<T> patchOperation = patchOperationIt.next();
           switch (patchOperation.op) {
                case PatchOperation.OP_DEL: {
                    indexToDelOperationMap.put(patchOperation.index, patchOperation);
                   break;
               }
                case PatchOperation.OP_ADD: {
                    indexToAddOperationMap.put(patchOperation.index, patchOperation);
                   break;
               }
                case PatchOperation.OP_REPLACE: {
                    indexToReplaceOperationMap.put(patchOperation.index, patchOperation);
                   break;
               }
                default: {
                    break;
               }
            }
        }
    }
    

    execute 做了上面介绍的算法操作:

    • 如果<0 ,则认为该old Item被删除了,记录为PatchOperation.OP_DEL,并记录该oldItem index到PatchOperation对象,加入到patchOperationList中。

    • 如果>0,则认为该newItem是新增的,记录为PatchOperation.OP_ADD,并记录该newItem index和value到PatchOperation对象,加入到patchOperationList中。

    • 如果=0,不会生成PatchOperation。

    最终得到了一个patchOperationList对象。经过一系列操作将patchOperationList转化为3个Map,分别为:indexToDelOperationMap,indexToAddOperationMap,indexToReplaceOperationMap。

    execute输出三个map,分别对应del、add、replace操作。

    然后simulatePatchOperation:

    public void simulatePatchOperation(int baseOffset) {
        boolean isNeedToMakeAlign = getTocSection(this.oldDex).isElementFourByteAligned;
       int oldIndex = 0;
       int patchedIndex = 0;
       int patchedOffset = baseOffset;
       while (oldIndex < this.oldItemCount || patchedIndex < this.newItemCount) {
            if (this.indexToAddOperationMap.containsKey(patchedIndex)) {
                PatchOperation<T> patchOperation = this.indexToAddOperationMap.get(patchedIndex);
               if (isNeedToMakeAlign) {
                    patchedOffset = SizeOf.roundToTimesOfFour(patchedOffset);
               }
                T newItem = patchOperation.newItem;
               int itemSize = getItemSize(newItem);
               updateIndexOrOffset(
                        this.newToPatchedIndexMap,
                       0,
                       getItemOffsetOrIndex(patchOperation.index, newItem),
                       0,
                       patchedOffset
                );
               ++patchedIndex;
               patchedOffset += itemSize;
           } else
           if (this.indexToReplaceOperationMap.containsKey(patchedIndex)) {
                PatchOperation<T> patchOperation = this.indexToReplaceOperationMap.get(patchedIndex);
               if (isNeedToMakeAlign) {
                    patchedOffset = SizeOf.roundToTimesOfFour(patchedOffset);
               }
                T newItem = patchOperation.newItem;
               int itemSize = getItemSize(newItem);
               updateIndexOrOffset(
                        this.newToPatchedIndexMap,
                       0,
                       getItemOffsetOrIndex(patchOperation.index, newItem),
                       0,
                       patchedOffset
                );
               ++patchedIndex;
               patchedOffset += itemSize;
           } else
           if (this.indexToDelOperationMap.containsKey(oldIndex)) {
                ++oldIndex;
           } else
           if (this.indexToReplaceOperationMap.containsKey(oldIndex)) {
                ++oldIndex;
           } else
           if (oldIndex < this.oldItemCount) {
                if (isNeedToMakeAlign) {
                    patchedOffset = SizeOf.roundToTimesOfFour(patchedOffset);
               }
                T oldItem = this.adjustedOldIndexedItemsWithOrigOrder[oldIndex].getValue();
               int itemSize = getItemSize(oldItem);
               int oldOffset = getItemOffsetOrIndex(oldIndex, oldItem);
               updateIndexOrOffset(
                        this.oldToPatchedIndexMap,
                       oldIndex,
                       oldOffset,
                       patchedIndex,
                       patchedOffset
                );
               int newIndex = oldIndex;
               if (this.oldIndexToNewIndexMap.containsKey(oldIndex)) {
                    newIndex = this.oldIndexToNewIndexMap.get(oldIndex);
               }
                int newOffset = oldOffset;
               if (this.oldOffsetToNewOffsetMap.containsKey(oldOffset)) {
                    newOffset = this.oldOffsetToNewOffsetMap.get(oldOffset);
               }
                updateIndexOrOffset(
                        this.newToPatchedIndexMap,
                       newIndex,
                       newOffset,
                       patchedIndex,
                       patchedOffset
                );
               ++oldIndex;
               ++patchedIndex;
               patchedOffset += itemSize;
           }
        }
        this.patchedSectionSize = SizeOf.roundToTimesOfFour(patchedOffset - baseOffset);
    }
    

    通过三个map最终确定patchedSectionSize,这个patchedSectionSize其实对应newDex的这个区域的size。

    再回过头来看: dexPatchGenerator.executeAndSaveTo(new patch.dex);

       public void executeAndSaveTo(OutputStream out) throws IOException {
            // Firstly, collect information of items we want to remove additionally
            // in new dex and set them to corresponding diff algorithm implementations.
            Pattern[] classNamePatterns = new Pattern[this.additionalRemovingClassPatternSet.size()];
            int classNamePatternCount = 0;
            for (String regExStr : this.additionalRemovingClassPatternSet) {
                classNamePatterns[classNamePatternCount++] = Pattern.compile(regExStr);
            }
    
            List<Integer> typeIdOfClassDefsToRemove = new ArrayList<>(classNamePatternCount);
            List<Integer> offsetOfClassDatasToRemove = new ArrayList<>(classNamePatternCount);
            for (ClassDef classDef : this.newDex.classDefs()) {
                String typeName = this.newDex.typeNames().get(classDef.typeIndex);
                for (Pattern pattern : classNamePatterns) {
                    if (pattern.matcher(typeName).matches()) {
                        typeIdOfClassDefsToRemove.add(classDef.typeIndex);
                        offsetOfClassDatasToRemove.add(classDef.classDataOffset);
                        break;
                    }
                }
            }
    
            ((ClassDefSectionDiffAlgorithm) this.classDefSectionDiffAlg)
                    .setTypeIdOfClassDefsToRemove(typeIdOfClassDefsToRemove);
            ((ClassDataSectionDiffAlgorithm) this.classDataSectionDiffAlg)
                    .setOffsetOfClassDatasToRemove(offsetOfClassDatasToRemove);
    
            // Then, run diff algorithms according to sections' dependencies.
    
            // Use size calculated by algorithms above or from dex file definition to
            // calculate sections' offset and patched dex size.
    
            // Calculate header and id sections size, so that we can work out
            // the base offset of typeLists Section.
            int patchedheaderSize = SizeOf.HEADER_ITEM;
            int patchedStringIdsSize = newDex.getTableOfContents().stringIds.size * SizeOf.STRING_ID_ITEM;
            int patchedTypeIdsSize = newDex.getTableOfContents().typeIds.size * SizeOf.TYPE_ID_ITEM;
    
            // Although simulatePatchOperation can calculate this value, since protoIds section
            // depends on typeLists section, we can't run protoIds Section's simulatePatchOperation
            // method so far. Instead we calculate protoIds section's size using information in newDex
            // directly.
            int patchedProtoIdsSize = newDex.getTableOfContents().protoIds.size * SizeOf.PROTO_ID_ITEM;
    
            int patchedFieldIdsSize = newDex.getTableOfContents().fieldIds.size * SizeOf.MEMBER_ID_ITEM;
            int patchedMethodIdsSize = newDex.getTableOfContents().methodIds.size * SizeOf.MEMBER_ID_ITEM;
            int patchedClassDefsSize = newDex.getTableOfContents().classDefs.size * SizeOf.CLASS_DEF_ITEM;
    
            int patchedIdSectionSize =
                    patchedStringIdsSize
                            + patchedTypeIdsSize
                            + patchedProtoIdsSize
                            + patchedFieldIdsSize
                            + patchedMethodIdsSize
                            + patchedClassDefsSize;
    
            this.patchedHeaderOffset = 0;
    
            // The diff works on each sections obey such procedure:
            //  1. Execute diff algorithms to calculate indices of items we need to add, del and replace.
            //  2. Execute patch algorithm simulation to calculate indices and offsets mappings that is
            //  necessary to next section's diff works.
    
            // Immediately do the patch simulation so that we can know:
            //  1. Indices and offsets mapping between old dex and patched dex.
            //  2. Indices and offsets mapping between new dex and patched dex.
            // These information will be used to do next diff works.
            this.patchedStringIdsOffset = patchedHeaderOffset + patchedheaderSize;
            if (this.oldDex.getTableOfContents().stringIds.isElementFourByteAligned) {
                this.patchedStringIdsOffset
                        = SizeOf.roundToTimesOfFour(this.patchedStringIdsOffset);
            }
            this.stringDataSectionDiffAlg.execute();
            this.patchedStringDataItemsOffset = patchedheaderSize + patchedIdSectionSize;
            if (this.oldDex.getTableOfContents().stringDatas.isElementFourByteAligned) {
                this.patchedStringDataItemsOffset
                        = SizeOf.roundToTimesOfFour(this.patchedStringDataItemsOffset);
            }
            this.stringDataSectionDiffAlg.simulatePatchOperation(this.patchedStringDataItemsOffset);
    
            this.typeIdSectionDiffAlg.execute();
            this.patchedTypeIdsOffset = this.patchedStringIdsOffset + patchedStringIdsSize;
            if (this.oldDex.getTableOfContents().typeIds.isElementFourByteAligned) {
                this.patchedTypeIdsOffset
                        = SizeOf.roundToTimesOfFour(this.patchedTypeIdsOffset);
            }
            this.typeIdSectionDiffAlg.simulatePatchOperation(this.patchedTypeIdsOffset);
    
            this.typeListSectionDiffAlg.execute();
            this.patchedTypeListsOffset
                    = patchedheaderSize
                    + patchedIdSectionSize
                    + this.stringDataSectionDiffAlg.getPatchedSectionSize();
            if (this.oldDex.getTableOfContents().typeLists.isElementFourByteAligned) {
                this.patchedTypeListsOffset
                        = SizeOf.roundToTimesOfFour(this.patchedTypeListsOffset);
            }
            this.typeListSectionDiffAlg.simulatePatchOperation(this.patchedTypeListsOffset);
    
            this.protoIdSectionDiffAlg.execute();
            this.patchedProtoIdsOffset = this.patchedTypeIdsOffset + patchedTypeIdsSize;
            if (this.oldDex.getTableOfContents().protoIds.isElementFourByteAligned) {
                this.patchedProtoIdsOffset = SizeOf.roundToTimesOfFour(this.patchedProtoIdsOffset);
            }
            this.protoIdSectionDiffAlg.simulatePatchOperation(this.patchedProtoIdsOffset);
    
            this.fieldIdSectionDiffAlg.execute();
            this.patchedFieldIdsOffset = this.patchedProtoIdsOffset + patchedProtoIdsSize;
            if (this.oldDex.getTableOfContents().fieldIds.isElementFourByteAligned) {
                this.patchedFieldIdsOffset = SizeOf.roundToTimesOfFour(this.patchedFieldIdsOffset);
            }
            this.fieldIdSectionDiffAlg.simulatePatchOperation(this.patchedFieldIdsOffset);
    
            this.methodIdSectionDiffAlg.execute();
            this.patchedMethodIdsOffset = this.patchedFieldIdsOffset + patchedFieldIdsSize;
            if (this.oldDex.getTableOfContents().methodIds.isElementFourByteAligned) {
                this.patchedMethodIdsOffset = SizeOf.roundToTimesOfFour(this.patchedMethodIdsOffset);
            }
            this.methodIdSectionDiffAlg.simulatePatchOperation(this.patchedMethodIdsOffset);
    
            this.annotationSectionDiffAlg.execute();
            this.patchedAnnotationItemsOffset
                    = this.patchedTypeListsOffset
                    + this.typeListSectionDiffAlg.getPatchedSectionSize();
            if (this.oldDex.getTableOfContents().annotations.isElementFourByteAligned) {
                this.patchedAnnotationItemsOffset
                        = SizeOf.roundToTimesOfFour(this.patchedAnnotationItemsOffset);
            }
            this.annotationSectionDiffAlg.simulatePatchOperation(this.patchedAnnotationItemsOffset);
    
            this.annotationSetSectionDiffAlg.execute();
            this.patchedAnnotationSetItemsOffset
                    = this.patchedAnnotationItemsOffset
                    + this.annotationSectionDiffAlg.getPatchedSectionSize();
            if (this.oldDex.getTableOfContents().annotationSets.isElementFourByteAligned) {
                this.patchedAnnotationSetItemsOffset
                        = SizeOf.roundToTimesOfFour(this.patchedAnnotationSetItemsOffset);
            }
            this.annotationSetSectionDiffAlg.simulatePatchOperation(
                    this.patchedAnnotationSetItemsOffset
            );
    
            this.annotationSetRefListSectionDiffAlg.execute();
            this.patchedAnnotationSetRefListItemsOffset
                    = this.patchedAnnotationSetItemsOffset
                    + this.annotationSetSectionDiffAlg.getPatchedSectionSize();
            if (this.oldDex.getTableOfContents().annotationSetRefLists.isElementFourByteAligned) {
                this.patchedAnnotationSetRefListItemsOffset
                        = SizeOf.roundToTimesOfFour(this.patchedAnnotationSetRefListItemsOffset);
            }
            this.annotationSetRefListSectionDiffAlg.simulatePatchOperation(
                    this.patchedAnnotationSetRefListItemsOffset
            );
    
            this.annotationsDirectorySectionDiffAlg.execute();
            this.patchedAnnotationsDirectoryItemsOffset
                    = this.patchedAnnotationSetRefListItemsOffset
                    + this.annotationSetRefListSectionDiffAlg.getPatchedSectionSize();
            if (this.oldDex.getTableOfContents().annotationsDirectories.isElementFourByteAligned) {
                this.patchedAnnotationsDirectoryItemsOffset
                        = SizeOf.roundToTimesOfFour(this.patchedAnnotationsDirectoryItemsOffset);
            }
            this.annotationsDirectorySectionDiffAlg.simulatePatchOperation(
                    this.patchedAnnotationsDirectoryItemsOffset
            );
    
            this.debugInfoSectionDiffAlg.execute();
            this.patchedDebugInfoItemsOffset
                    = this.patchedAnnotationsDirectoryItemsOffset
                    + this.annotationsDirectorySectionDiffAlg.getPatchedSectionSize();
            if (this.oldDex.getTableOfContents().debugInfos.isElementFourByteAligned) {
                this.patchedDebugInfoItemsOffset
                        = SizeOf.roundToTimesOfFour(this.patchedDebugInfoItemsOffset);
            }
            this.debugInfoSectionDiffAlg.simulatePatchOperation(this.patchedDebugInfoItemsOffset);
    
            this.codeSectionDiffAlg.execute();
            this.patchedCodeItemsOffset
                    = this.patchedDebugInfoItemsOffset
                    + this.debugInfoSectionDiffAlg.getPatchedSectionSize();
            if (this.oldDex.getTableOfContents().codes.isElementFourByteAligned) {
                this.patchedCodeItemsOffset = SizeOf.roundToTimesOfFour(this.patchedCodeItemsOffset);
            }
            this.codeSectionDiffAlg.simulatePatchOperation(this.patchedCodeItemsOffset);
    
            this.classDataSectionDiffAlg.execute();
            this.patchedClassDataItemsOffset
                    = this.patchedCodeItemsOffset
                    + this.codeSectionDiffAlg.getPatchedSectionSize();
            if (this.oldDex.getTableOfContents().classDatas.isElementFourByteAligned) {
                this.patchedClassDataItemsOffset
                        = SizeOf.roundToTimesOfFour(this.patchedClassDataItemsOffset);
            }
            this.classDataSectionDiffAlg.simulatePatchOperation(this.patchedClassDataItemsOffset);
    
            this.encodedArraySectionDiffAlg.execute();
            this.patchedEncodedArrayItemsOffset
                    = this.patchedClassDataItemsOffset
                    + this.classDataSectionDiffAlg.getPatchedSectionSize();
            if (this.oldDex.getTableOfContents().encodedArrays.isElementFourByteAligned) {
                this.patchedEncodedArrayItemsOffset
                        = SizeOf.roundToTimesOfFour(this.patchedEncodedArrayItemsOffset);
            }
            this.encodedArraySectionDiffAlg.simulatePatchOperation(this.patchedEncodedArrayItemsOffset);
    
            this.classDefSectionDiffAlg.execute();
            this.patchedClassDefsOffset = this.patchedMethodIdsOffset + patchedMethodIdsSize;
            if (this.oldDex.getTableOfContents().classDefs.isElementFourByteAligned) {
                this.patchedClassDefsOffset = SizeOf.roundToTimesOfFour(this.patchedClassDefsOffset);
            }
    
            // Calculate any values we still know nothing about them.
            this.patchedMapListOffset
                    = this.patchedEncodedArrayItemsOffset
                    + this.encodedArraySectionDiffAlg.getPatchedSectionSize();
            if (this.oldDex.getTableOfContents().mapList.isElementFourByteAligned) {
                this.patchedMapListOffset = SizeOf.roundToTimesOfFour(this.patchedMapListOffset);
            }
            int patchedMapListSize = newDex.getTableOfContents().mapList.byteCount;
    
            this.patchedDexSize = this.patchedMapListOffset + patchedMapListSize;
    
            // Finally, write results to patch file.
            writeResultToStream(out);
        }
    

    最终会走writeResultToStream

    private void writeResultToStream(OutputStream os) throws IOException {
        DexDataBuffer buffer = new DexDataBuffer();
       buffer.write(DexPatchFile.MAGIC);
       buffer.writeShort(DexPatchFile.CURRENT_VERSION);
       buffer.writeInt(this.patchedDexSize);
       // we will return here to write firstChunkOffset later.
       int posOfFirstChunkOffsetField = buffer.position();
       buffer.writeInt(0);
       buffer.writeInt(this.patchedStringIdsOffset);
       buffer.writeInt(this.patchedTypeIdsOffset);
       buffer.writeInt(this.patchedProtoIdsOffset);
       buffer.writeInt(this.patchedFieldIdsOffset);
       buffer.writeInt(this.patchedMethodIdsOffset);
       buffer.writeInt(this.patchedClassDefsOffset);
       buffer.writeInt(this.patchedMapListOffset);
       buffer.writeInt(this.patchedTypeListsOffset);
       buffer.writeInt(this.patchedAnnotationSetRefListItemsOffset);
       buffer.writeInt(this.patchedAnnotationSetItemsOffset);
       buffer.writeInt(this.patchedClassDataItemsOffset);
       buffer.writeInt(this.patchedCodeItemsOffset);
       buffer.writeInt(this.patchedStringDataItemsOffset);
       buffer.writeInt(this.patchedDebugInfoItemsOffset);
       buffer.writeInt(this.patchedAnnotationItemsOffset);
       buffer.writeInt(this.patchedEncodedArrayItemsOffset);
       buffer.writeInt(this.patchedAnnotationsDirectoryItemsOffset);
       buffer.write(this.oldDex.computeSignature(false));
       int firstChunkOffset = buffer.position();
       buffer.position(posOfFirstChunkOffsetField);
       buffer.writeInt(firstChunkOffset);
       buffer.position(firstChunkOffset);
       writePatchOperations(buffer, this.stringDataSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.typeIdSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.typeListSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.protoIdSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.fieldIdSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.methodIdSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.annotationSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.annotationSetSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.annotationSetRefListSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.annotationsDirectorySectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.debugInfoSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.codeSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.classDataSectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.encodedArraySectionDiffAlg.getPatchOperationList());
       writePatchOperations(buffer, this.classDefSectionDiffAlg.getPatchOperationList());
       byte[] bufferData = buffer.array();
       os.write(bufferData);
       os.flush();
    }
    

    写内容:
    以writePatchOperations(buffer, this.stringDataSectionDiffAlg.getPatchOperationList());为例:

    public List<PatchOperation<T>> getPatchOperationList() {
        return this.patchOperationList;
    }
    

    先写了MAGIC和Version用于校验该文件是一个patch file;接下来为patchedDexSize和各种offset进行赋值;最后定位到数据区(firstChunkOffset),还记得写的时候,该字段在第四个位置。

    定位到该位置后,后面读取的就是数据了,数据存的时候按照如下格式存储的:

    del操作的个数,每个del的index
    add操作的个数,每个add的index
    replace操作的个数,每个需要replace的index
    最后依次写入newItemList.

    相关文章

      网友评论

          本文标题:热修复框架 - Tinker DexDiff算法浅析

          本文链接:https://www.haomeiwen.com/subject/rmjsjktx.html