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

代码: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

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

execute:

public void execute() {
    this.patchOperationList.clear();
   this.adjustedOldIndexedItemsWithOrigOrder = collectSectionItems(this.oldDex, true);
   this.oldItemCount = this.adjustedOldIndexedItemsWithOrigOrder.length;
   AbstractMap.SimpleEntry[] adjustedOldIndexedItems = new AbstractMap.SimpleEntry[this.oldItemCount];
   System.arraycopy(this.adjustedOldIndexedItemsWithOrigOrder, 0, adjustedOldIndexedItems, 0, this.oldItemCount);
   Arrays.sort(adjustedOldIndexedItems, this.comparatorForItemDiff);
   AbstractMap.SimpleEntry[] 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 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 oldIndexedItem = adjustedOldIndexedItems[oldCursor++];
               int deletedIndex = oldIndexedItem.getKey();
               int deletedOffset = getItemOffsetOrIndex(deletedIndex, oldIndexedItem.getValue());
               this.patchOperationList.add(new PatchOperation(PatchOperation.OP_DEL, deletedIndex));
               markDeletedIndexOrOffset(this.oldToPatchedIndexMap, deletedIndex, deletedOffset);
           }
        } else {
            AbstractMap.SimpleEntry oldIndexedItem = adjustedOldIndexedItems[oldCursor];
           AbstractMap.SimpleEntry 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(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> patchOperationIt = this.patchOperationList.iterator();
   PatchOperation prevPatchOperation = null;
   while (patchOperationIt.hasNext()) {
        PatchOperation 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 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 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 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 typeIdOfClassDefsToRemove = new ArrayList<>(classNamePatternCount);
        List 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> getPatchOperationList() {
    return this.patchOperationList;
}

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

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

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

你可能感兴趣的:(热修复框架 - Tinker DexDiff算法浅析)