代码:tinker 1.9.14.7
这篇文章主要是学习@鸿洋 @dodola 写的相关文章:
Android 热修复 Tinker 源码分析之DexDiff / DexPatch
Tinker
一、dex文件结构
用010 Editor 看下dex文件结构
header 描述了 dex 文件信息,和其他各个区的索引id大小和偏移量,同时可校验文件格式是否正确,文件是否被篡改等。
image.pngstring_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.
网友评论