Tinker patch dex 生成

1、soDecoder.patch(oldFile, file.toFile());   UniqueDexDiffDecoder

dexPatchDecoder = new UniqueDexDiffDecoder(config, prePath + TypedValue.DEX_META_FILE, TypedValue.DEX_LOG_FILE);
soPatchDecoder = new BsDiffDecoder(config, prePath + TypedValue.SO_META_FILE, TypedValue.SO_LOG_FILE);
resPatchDecoder = new ResDiffDecoder(config, prePath + TypedValue.RES_META_TXT, TypedValue.RES_LOG_FILE);
resDuplicateFiles = new ArrayList<>();

 

2、UniqueDexDiffDecoder.java  patch 结束后进行检测,不允许同名,基类是DexDiffDecoder

@Override
public boolean patch(File oldFile, File newFile) throws IOException, TinkerPatchException {
    boolean added = super.patch(oldFile, newFile);
    if (added) {
        String name = newFile.getName();
        if (addedDexFiles.contains(name)) {
            throw new TinkerPatchException("illegal dex name, dex name should be unique, dex:" + name);
        } else {
            addedDexFiles.add(name);
        }
    }
    return added;
}

3、DexDiffDecoder.java patch 校验和生成md5  onAllPatchesEnd执行patch生成,和test.dex生成

@SuppressWarnings("NewApi")
@Override
public boolean patch(final File oldFile, final File newFile) throws IOException, TinkerPatchException {
    final String dexName = getRelativeDexName(oldFile, newFile);

    // first of all, we should check input files if excluded classes were modified.
    Logger.d("Check for loader classes in dex: %s", dexName);

    try {
        excludedClassModifiedChecker.checkIfExcludedClassWasModifiedInNewDex(oldFile, newFile);
    } catch (IOException e) {
        throw new TinkerPatchException(e);
    } catch (TinkerPatchException e) {
        if (config.mIgnoreWarning) {
            Logger.e("Warning:ignoreWarning is true, but we found %s", e.getMessage());
        } else {
            Logger.e("Warning:ignoreWarning is false, but we found %s", e.getMessage());
            throw e;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    // If corresponding new dex was completely deleted, just return false.
    // don't process 0 length dex
    if (newFile == null || !newFile.exists() || newFile.length() == 0) {
        return false;
    }

    File dexDiffOut = getOutputPath(newFile).toFile();

    final String newMd5 = getRawOrWrappedDexMD5(newFile);

    //new add file
    if (oldFile == null || !oldFile.exists() || oldFile.length() == 0) {
        hasDexChanged = true;
        copyNewDexAndLogToDexMeta(newFile, newMd5, dexDiffOut);
        return true;
    }

    final String oldMd5 = getRawOrWrappedDexMD5(oldFile);

    if ((oldMd5 != null && !oldMd5.equals(newMd5)) || (oldMd5 == null && newMd5 != null)) {
        hasDexChanged = true;
        if (oldMd5 != null) {
            collectAddedOrDeletedClasses(oldFile, newFile);
        }
    }

    RelatedInfo relatedInfo = new RelatedInfo();
    relatedInfo.oldMd5 = oldMd5;
    relatedInfo.newMd5 = newMd5;

    // collect current old dex file and corresponding new dex file for further processing.
    oldAndNewDexFilePairList.add(new AbstractMap.SimpleEntry<>(oldFile, newFile));

    dexNameToRelatedInfoMap.put(dexName, relatedInfo);

    return true;
}

@Override
public void onAllPatchesEnd() throws Exception {
    if (!hasDexChanged) {
        Logger.d("No dexes were changed, nothing needs to be done next.");
        return;
    }

    if (config.mIsProtectedApp) {
        generateChangedClassesDexFile();
    } else {
        generatePatchInfoFile();
    }

    addTestDex();
}

4、接下来进入generatePatchedDexInfoFile
      logDexesToDexMeta 生成assets的 dex_meta.txt 

@SuppressWarnings("NewApi")
private void generatePatchInfoFile() throws IOException {
    generatePatchedDexInfoFile();

    // generateSmallPatchedDexInfoFile is blocked by issue we found in ART environment
    // which indicates that if inline optimization is done on patched class, some error
    // such as crash, ClassCastException, mistaken string fetching, etc. would happen.
    //
    // Instead, we will log all classN dexes as 'copy directly' in dex-meta, so that
    // tinker patch applying procedure will copy them out and load them in ART environment.

    //generateSmallPatchedDexInfoFile();

    logDexesToDexMeta();

    checkCrossDexMovingClasses();
}

5、generatePatchedDexInfoFile 执行到 DexPatchGenerator.java

@SuppressWarnings("NewApi")
private void generatePatchedDexInfoFile() throws IOException {
    // Generate dex diff out and full patched dex if a pair of dex is different.
    for (AbstractMap.SimpleEntry<File, File> oldAndNewDexFilePair : oldAndNewDexFilePairList) {
        File oldFile = oldAndNewDexFilePair.getKey();
        File newFile = oldAndNewDexFilePair.getValue();
        final String dexName = getRelativeDexName(oldFile, newFile);
        RelatedInfo relatedInfo = dexNameToRelatedInfoMap.get(dexName);
        if (!relatedInfo.oldMd5.equals(relatedInfo.newMd5)) {
            diffDexPairAndFillRelatedInfo(oldFile, newFile, relatedInfo);
        } else {
            // In this case newDexFile is the same as oldDexFile, but we still
            // need to treat it as patched dex file so that the SmallPatchGenerator
            // can analyze which class of this dex should be kept in small patch.
            relatedInfo.newOrFullPatchedFile = newFile;
            relatedInfo.newOrFullPatchedMd5 = relatedInfo.newMd5;
            relatedInfo.newOrFullPatchedCRC = FileOperation.getFileCrc32(newFile);
        }
    }
}
private void diffDexPairAndFillRelatedInfo(File oldDexFile, File newDexFile, RelatedInfo relatedInfo) {
    File tempFullPatchDexPath = new File(config.mOutFolder + File.separator + TypedValue.DEX_TEMP_PATCH_DIR);
    final String dexName = getRelativeDexName(oldDexFile, newDexFile);

    File dexDiffOut = getOutputPath(newDexFile).toFile();
    ensureDirectoryExist(dexDiffOut.getParentFile());

    try {
        DexPatchGenerator dexPatchGen = new DexPatchGenerator(oldDexFile, newDexFile);
        dexPatchGen.setAdditionalRemovingClassPatterns(config.mDexLoaderPattern);

        logWriter.writeLineToInfoFile(
                String.format(
                        "Start diff between [%s] as old and [%s] as new:",
                        getRelativeStringBy(oldDexFile, config.mTempUnzipOldDir),
                        getRelativeStringBy(newDexFile, config.mTempUnzipNewDir)
                )
        );

        dexPatchGen.executeAndSaveTo(dexDiffOut);
    } catch (Exception e) {
        throw new TinkerPatchException(e);
    }

    if (!dexDiffOut.exists()) {
        throw new TinkerPatchException("can not find the diff file:" + dexDiffOut.getAbsolutePath());
    }

    relatedInfo.dexDiffFile = dexDiffOut;
    relatedInfo.dexDiffMd5 = MD5.getMD5(dexDiffOut);
    Logger.d("\nGen %s patch file:%s, size:%d, md5:%s", dexName, relatedInfo.dexDiffFile.getAbsolutePath(), relatedInfo.dexDiffFile.length(), relatedInfo.dexDiffMd5);

    File tempFullPatchedDexFile = new File(tempFullPatchDexPath, dexName);
    if (!tempFullPatchedDexFile.exists()) {
        ensureDirectoryExist(tempFullPatchedDexFile.getParentFile());
    }

    try {
        new DexPatchApplier(oldDexFile, dexDiffOut).executeAndSaveTo(tempFullPatchedDexFile);

        Logger.d(
                String.format("Verifying if patched new dex is logically the same as original new dex: %s ...", getRelativeStringBy(newDexFile, config.mTempUnzipNewDir))
        );

        Dex origNewDex = new Dex(newDexFile);
        Dex patchedNewDex = new Dex(tempFullPatchedDexFile);
        checkDexChange(origNewDex, patchedNewDex);

        relatedInfo.newOrFullPatchedFile = tempFullPatchedDexFile;
        relatedInfo.newOrFullPatchedMd5 = MD5.getMD5(tempFullPatchedDexFile);
        relatedInfo.newOrFullPatchedCRC = FileOperation.getFileCrc32(tempFullPatchedDexFile);
    } catch (Exception e) {
        e.printStackTrace();
        throw new TinkerPatchException(
                "Failed to generate temporary patched dex, which makes MD5 generating procedure of new dex failed, either.", e
        );
    }

    if (!tempFullPatchedDexFile.exists()) {
        throw new TinkerPatchException("can not find the temporary full patched dex file:" + tempFullPatchedDexFile.getAbsolutePath());
    }
    Logger.d("\nGen %s for dalvik full dex file:%s, size:%d, md5:%s", dexName, tempFullPatchedDexFile.getAbsolutePath(), tempFullPatchedDexFile.length(), relatedInfo.newOrFullPatchedMd5);
}

 

6、

DexPatchGenerator.java
tinker 基于dex的结构 对以下项分布进行差分

patchedStringIdsOffset
patchedTypeIdsOffset
patchedProtoIdsOffset
patchedFieldIdsOffset
patchedMethodIdsOffset
patchedClassDefsOffset

patchedStringDataItemsOffset
patchedTypeListsOffset
patchedAnnotationItemsOffset
patchedAnnotationSetItemsOffset
patchedAnnotationSetRefListItemsOffset
patchedAnnotationsDirectoryItemsOffset
patchedDebugInfoItemsOffset
patchedCodeItemsOffset
patchedClassDataItemsOffset
patchedEncodedArrayItemsOffset
patchedMapListOffset

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);
}

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();
}

7、tinker-dex-dump 查看dex的数据结构如下

 

8、DexSectionDiffAlgorithm.java 
排序后对olddex 和newdex item进行比较,确认那些item 增加、删除和替换

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;
            }
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值