热更新Tinker研究(七):Dex的patch文件生成
来源:互联网 发布:阿里云建立pptp 编辑:程序博客网 时间:2024/05/20 15:37
热更新Tinker研究(一):运行tinker-sample-android
热更新Tinker研究(二):结合源码学习Dex格式
热更新Tinker研究(三):加载补丁
热更新Tinker研究(四):TinkerLoader
热更新Tinker研究(五):Application的隔离
热更新Tinker研究(六):TinkerPatchPlugin
热更新Tinker研究(七):Dex的patch文件生成
热更新Tinker研究(八):res和so的patch文件生成
热更新Tinker研究(九):Dex文件的patch
热更新Tinker研究(十):Res文件的patch
热更新Tinker研究(十一):so文件的patch
热更新Tinker研究(七):Dex的patch文件生成
ApkDecoder中的dexPatchDecoder负责dex的patch生成工作,dexPatchDecoder实际上是UniqueDexDiffDecoder类型。这一系列相关的类的关系如下图所示。
BaseDecoder中有三个抽象方法,onAllPatchesStart(), patch(File oldFile, File newFile),onAllPatchesEnd()。其中onAllPatchesStart()表示patch()进行前,patch()是正式的patch方法,onAllPatchesEnd()用于处理patch完成后的后续工作。
UniqueDexDiffDecoder只是在DexDiffDecoder的基础上,用一个list保存处理过的dex,来保证dex文件的唯一性。
整个过程可以分为以下步骤来讲解
一、DexClassesComparator
DexClassesComparator是用于比较dex差异的主要类,其中也几个比较重要的成员变量,
private final Set<Pattern> patternsOfClassDescToCheck = new HashSet<>(); //检测的集合 private final Set<Pattern> patternsOfIgnoredRemovedClassDesc = new HashSet<>(); //忽略的集合
检测的集合和忽略的集合可以让我们灵活使用,不同的场景下设置不同的参数。
该类比较重要的比较方法具体原理如下,
首先会将类描述(带包名的类名)按照一定的前面的过滤条件放到对应的集合里面,old和new Dex都会处理,
// Map classDesc and typeIndex to classInfo // and collect typeIndex of classes to check in oldDexes. for (Dex oldDex : oldDexGroup.dexes) { int classDefIndex = 0; for (ClassDef oldClassDef : oldDex.classDefs()) { String desc = oldDex.typeNames().get(oldClassDef.typeIndex); if (Utils.isStringMatchesPatterns(desc, patternsOfClassDescToCheck)) { if (!oldDescriptorOfClassesToCheck.add(desc)) { throw new IllegalStateException( String.format( "duplicate class descriptor [%s] in different old dexes.", desc ) ); } } DexClassInfo classInfo = new DexClassInfo(desc, classDefIndex, oldClassDef, oldDex); ++classDefIndex; oldClassDescriptorToClassInfoMap.put(desc, classInfo); } } // Map classDesc and typeIndex to classInfo // and collect typeIndex of classes to check in newDexes. for (Dex newDex : newDexGroup.dexes) { int classDefIndex = 0; for (ClassDef newClassDef : newDex.classDefs()) { String desc = newDex.typeNames().get(newClassDef.typeIndex); if (Utils.isStringMatchesPatterns(desc, patternsOfClassDescToCheck)) { if (!newDescriptorOfClassesToCheck.add(desc)) { throw new IllegalStateException( String.format( "duplicate class descriptor [%s] in different new dexes.", desc ) ); } } DexClassInfo classInfo = new DexClassInfo(desc, classDefIndex, newClassDef, newDex); ++classDefIndex; newClassDescriptorToClassInfoMap.put(desc, classInfo); } }
然后再是得到被删除的类,其实就是oldDescriptorOfClassesToCheck - newDescriptorOfClassesToCheck,差集部分就是我们需要的。
//oldDescriptorOfClassesToCheck - newDescriptorOfClassesToCheck差集 Set<String> deletedClassDescs = new HashSet<>(oldDescriptorOfClassesToCheck); deletedClassDescs.removeAll(newDescriptorOfClassesToCheck); for (String desc : deletedClassDescs) { // These classes are deleted as we expect to, so we remove them // from result. if (Utils.isStringMatchesPatterns(desc, patternsOfIgnoredRemovedClassDesc)) { logger.i(TAG, "Ignored deleted class: %s", desc); continue; } else { logger.i(TAG, "Deleted class: %s", desc); } deletedClassInfoList.add(oldClassDescriptorToClassInfoMap.get(desc)); }
再是找到被增加的类,newDescriptorOfClassesToCheck - oldDescriptorOfClassesToCheck,求差集,
//被增加的类:newDescriptorOfClassesToCheck - oldDescriptorOfClassesToCheck Set<String> addedClassDescs = new HashSet<>(newDescriptorOfClassesToCheck); addedClassDescs.removeAll(oldDescriptorOfClassesToCheck); for (String desc : addedClassDescs) { logger.i(TAG, "Added class: %s", desc); addedClassInfoList.add(newClassDescriptorToClassInfoMap.get(desc)); }
要找到被改变的类,首先要找到两个集合的交集,再进行比对,如果发生了改变,就符合被改变的类的条件,加入集合。
//被改变的类 求交集 Set<String> mayBeChangedClassDescs = new HashSet<>(oldDescriptorOfClassesToCheck); mayBeChangedClassDescs.retainAll(newDescriptorOfClassesToCheck); for (String desc : mayBeChangedClassDescs) { DexClassInfo oldClassInfo = oldClassDescriptorToClassInfoMap.get(desc); DexClassInfo newClassInfo = newClassDescriptorToClassInfoMap.get(desc); switch (compareMode) { //?什么区别? case COMPARE_MODE_NORMAL: { if (!isSameClass( oldClassInfo.owner, newClassInfo.owner, oldClassInfo.classDef, newClassInfo.classDef )) { logger.i(TAG, "Changed class: %s", desc); changedClassDescToClassInfosMap.put( desc, new DexClassInfo[]{oldClassInfo, newClassInfo} ); } break; } case COMPARE_MODE_CAUSE_REF_CHANGE_ONLY: { if (isClassChangeAffectedToRef( oldClassInfo.owner, newClassInfo.owner, oldClassInfo.classDef, newClassInfo.classDef )) { logger.i(TAG, "Ref-changed class: %s", desc); changedClassDescToClassInfosMap.put( desc, new DexClassInfo[]{oldClassInfo, newClassInfo} ); } break; } } }
二、checkIfExcludedClassWasModifiedInNewDex
主要检测没有被包含差分范围内的类,这里一般就是指设定的loader。这里的检测规则如下,
loader相关类必须存在在oldDex文件中的primary dex,也必须要让它们在新的primary dex中保持一致,
有下面一些情况就会被判断为错误发生:
- primary old dex找不到
- primary new dex找不到
- primary old dex 中不存在loader classes
- primary new dex 中存在loader classes
- loader classes 在primary new dex中不删改
- loader classes 在secondary old dexes中被发现
- loader calsses 在secondary new dexes中被发现
相关检测代码如下,主要是利用DexClassesComparator,这里会把loader classes设置为patternsOfClassDescToCheck。
“`java
boolean isPrimaryDex = isPrimaryDex((oldFile == null ? newFile : oldFile));
if (isPrimaryDex) { if (oldFile == null) { stmCode = STMCODE_ERROR_PRIMARY_OLD_DEX_IS_MISSING; } else if (newFile == null) { stmCode = STMCODE_ERROR_PRIMARY_NEW_DEX_IS_MISSING; } else { dexCmptor.startCheck(oldDex, newDex); deletedClassInfos = dexCmptor.getDeletedClassInfos(); addedClassInfos = dexCmptor.getAddedClassInfos(); changedClassInfosMap = dexCmptor.getChangedClassDescToInfosMap(); // All loader classes are in new dex, while none of them in old one. //这里正确的情况应该是三个infos都为空 //如果deletedClassInfos和changedClassInfosMap为空而addedClassInfos不为空,表示loader classes不全在primary old dex中 if (deletedClassInfos.isEmpty() && changedClassInfosMap.isEmpty() && !addedClassInfos.isEmpty()) { stmCode = STMCODE_ERROR_LOADER_CLASS_NOT_IN_PRIMARY_OLD_DEX; } else { if (deletedClassInfos.isEmpty() && addedClassInfos.isEmpty()) { // class descriptor is completely matches, see if any contents changes. if (changedClassInfosMap.isEmpty()) { stmCode = STMCODE_END; } else { //如果有改变 stmCode = STMCODE_ERROR_LOADER_CLASS_CHANGED; } } else { //有删除 或者 增加 stmCode = STMCODE_ERROR_LOADER_CLASS_IN_PRIMARY_DEX_MISMATCH; } } } } else { //后面主要检测loader classes是否存在于secondary dexes Set<Pattern> patternsOfClassDescToCheck = new HashSet<>(); for (String patternStr : config.mDexLoaderPattern) { patternsOfClassDescToCheck.add( Pattern.compile( PatternUtils.dotClassNamePatternToDescriptorRegEx(patternStr) ) ); } if (oldDex != null) { oldClassesDescToCheck.clear(); for (ClassDef classDef : oldDex.classDefs()) { String desc = oldDex.typeNames().get(classDef.typeIndex); if (Utils.isStringMatchesPatterns(desc, patternsOfClassDescToCheck)) { oldClassesDescToCheck.add(desc); } } if (!oldClassesDescToCheck.isEmpty()) { stmCode = STMCODE_ERROR_LOADER_CLASS_FOUND_IN_SECONDARY_OLD_DEX; break; } } if (newDex != null) { newClassesDescToCheck.clear(); for (ClassDef classDef : newDex.classDefs()) { String desc = newDex.typeNames().get(classDef.typeIndex); if (Utils.isStringMatchesPatterns(desc, patternsOfClassDescToCheck)) { newClassesDescToCheck.add(desc); } } if (!newClassesDescToCheck.isEmpty()) { stmCode = STMCODE_ERROR_LOADER_CLASS_FOUND_IN_SECONDARY_NEW_DEX; break; } } stmCode = STMCODE_END; }
“`
三、collectAddedOrDeletedClasses
这里主要是为了得到增加和删除的classes,和DexClassesComparator中原理类似,也是利用两个集合求差集。
Dex oldDex = new Dex(oldFile); Dex newDex = new Dex(newFile); Set<String> oldClassDescs = new HashSet<>(); for (ClassDef oldClassDef : oldDex.classDefs()) { oldClassDescs.add(oldDex.typeNames().get(oldClassDef.typeIndex)); } Set<String> newClassDescs = new HashSet<>(); for (ClassDef newClassDef : newDex.classDefs()) { newClassDescs.add(newDex.typeNames().get(newClassDef.typeIndex)); } // newClassDescs - oldClassDescs得到 增加的类 Set<String> addedClassDescs = new HashSet<>(newClassDescs); addedClassDescs.removeAll(oldClassDescs); //oldClassDescs - newClassDescs得到 删除的类 Set<String> deletedClassDescs = new HashSet<>(oldClassDescs); deletedClassDescs.removeAll(newClassDescs); for (String addedClassDesc : addedClassDescs) { if (addedClassDescToDexNameMap.containsKey(addedClassDesc)) { throw new TinkerPatchException( String.format( "Class Duplicate. Class [%s] is added in both new dex: [%s] and [%s]. Please check your newly apk.", addedClassDesc, addedClassDescToDexNameMap.get(addedClassDesc), newFile.toString() ) ); } else { addedClassDescToDexNameMap.put(addedClassDesc, newFile.toString()); } } for (String deletedClassDesc : deletedClassDescs) { if (deletedClassDescToDexNameMap.containsKey(deletedClassDesc)) { throw new TinkerPatchException( String.format( "Class Duplicate. Class [%s] is deleted in both old dex: [%s] and [%s]. Please check your base apk.", deletedClassDesc, addedClassDescToDexNameMap.get(deletedClassDesc), oldFile.toString() ) ); } else { deletedClassDescToDexNameMap.put(deletedClassDesc, newFile.toString()); } }
四、generatePatchInfoFile
generatePatchedDexInfoFile
根据oldDex和newDex的对应关系,来生成dex_meta.txt中的列表,其中
oldAndNewDexFilePairList保存这oldDex和newDex中的对应关系,具体代码
private void generatePatchedDexInfoFile() { // 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; } } }
logDexesToDexMeta
这个方法主要是根据前面记录的RelatedInfo,来把dex的patch信息写入dex_meta.txt中,这里会针对不同的情况进行不同的操作,部分代码如下
for (AbstractMap.SimpleEntry<File, File> oldAndNewDexFilePair : oldAndNewDexFilePairList) { final File oldDexFile = oldAndNewDexFilePair.getKey(); final File newDexFile = oldAndNewDexFilePair.getValue(); final String dexName = getRelativeDexName(oldDexFile, newDexFile); final RelatedInfo relatedInfo = dexNameToRelatedInfoMap.get(dexName); if (!relatedInfo.oldMd5.equals(relatedInfo.newMd5)) { //logToDexMeta(newDexFile, oldDexFile, relatedInfo.dexDiffFile, relatedInfo.newOrFullPatchedMd5, relatedInfo.smallPatchedMd5, relatedInfo.dexDiffMd5); logToDexMeta(newDexFile, oldDexFile, relatedInfo.dexDiffFile, relatedInfo.newOrFullPatchedMd5, relatedInfo.newOrFullPatchedMd5, relatedInfo.dexDiffMd5); } else { // For class N dexes, if new dex is the same as old dex, we should log it as 'copy directly' // in dex meta to fix problems in Art environment. if (realClassNDexFiles.contains(oldDexFile)) { //if (!"0".equals(relatedInfo.smallPatchedMd5)) { // logToDexMeta(newDexFile, oldDexFile, null, "0", relatedInfo.smallPatchedMd5, "0"); //} // Bugfix: However, if what we would copy directly is main dex, we should do an additional diff operation // so that patch applier would help us remove all loader classes of it in runtime. if (dexName.equals(DexFormat.DEX_IN_JAR_NAME)) { Logger.d("\nDo additional diff on main dex to remove loader classes in it."); diffDexPairAndFillRelatedInfo(oldDexFile, newDexFile, relatedInfo); logToDexMeta(newDexFile, oldDexFile, relatedInfo.dexDiffFile, relatedInfo.newOrFullPatchedMd5, relatedInfo.newOrFullPatchedMd5, relatedInfo.dexDiffMd5); } else { logToDexMeta(newDexFile, oldDexFile, null, "0", relatedInfo.oldMd5, "0"); } } } }
由于md5不同时,前面的方法已经生成了classes.dex等差分文件,这里只需要把差异信息记录到dex_meta.txt中。当md5相同时,对于mainDex,需要移除掉loader classes之后,重新生成比对信息。
五、DexPatchGenerator
dex的差分工作主要在DexPatchGenerator中完成,
public class DexPatchGenerator { private static final String TAG = "DexPatchGenerator"; private final Dex oldDex; private final Dex newDex; private final DexPatcherLogger logger = new DexPatcherLogger(); private DexSectionDiffAlgorithm<StringData> stringDataSectionDiffAlg; private DexSectionDiffAlgorithm<Integer> typeIdSectionDiffAlg; private DexSectionDiffAlgorithm<ProtoId> protoIdSectionDiffAlg; private DexSectionDiffAlgorithm<FieldId> fieldIdSectionDiffAlg; private DexSectionDiffAlgorithm<MethodId> methodIdSectionDiffAlg; private DexSectionDiffAlgorithm<ClassDef> classDefSectionDiffAlg; private DexSectionDiffAlgorithm<TypeList> typeListSectionDiffAlg; private DexSectionDiffAlgorithm<AnnotationSetRefList> annotationSetRefListSectionDiffAlg; private DexSectionDiffAlgorithm<AnnotationSet> annotationSetSectionDiffAlg; private DexSectionDiffAlgorithm<ClassData> classDataSectionDiffAlg; private DexSectionDiffAlgorithm<Code> codeSectionDiffAlg; private DexSectionDiffAlgorithm<DebugInfoItem> debugInfoSectionDiffAlg; private DexSectionDiffAlgorithm<Annotation> annotationSectionDiffAlg; private DexSectionDiffAlgorithm<EncodedValue> encodedArraySectionDiffAlg; private DexSectionDiffAlgorithm<AnnotationsDirectory> annotationsDirectorySectionDiffAlg; ......}
DexSectionDiffAlgorithm
DexSectionDiffAlgorithm负责某一个section的差分工作,
调整dex文件中元素的顺序
在比较两个dex文件差异之前,需要按照固定的顺序去对dex中sectionItem进行排序
//按照一定的顺序调整 item的位置 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);
其中有这么几个数据结构来保存对应关系,
/** * SparseIndexMap for mapping items between old dex and new dex. * e.g. item.oldIndex => item.newIndex */ private final SparseIndexMap oldToNewIndexMap; /** * SparseIndexMap for mapping items between old dex and patched dex. * e.g. item.oldIndex => item.patchedIndex */ private final SparseIndexMap oldToPatchedIndexMap; /** * SparseIndexMap for mapping items between new dex and patched dex. * e.g. item.newIndex => item.newIndexInPatchedDex */ private final SparseIndexMap newToPatchedIndexMap; /** * SparseIndexMap for mapping items in new dex when skip items. */ private final SparseIndexMap selfIndexMapForSkip;
由于patchedDex和newDex不是完全相同,数据结构以及内容相同,但是排列顺序不一定相同,也就是patchedDex最终合成的dex和newDex的执行效果相同,但是md5不一定要相同,所以这里需要上面这么几个集合来保存oldDex,patchedDex和newDex的对应关系。
调整item顺序之前中有个关键步骤,就是去收集一个dex文件中的item,这里实际就是按照patched需要的顺序去调整oldDex和newDex中的数据结构。
private AbstractMap.SimpleEntry<Integer, T>[] collectSectionItems(Dex dex, boolean isOldDex) { TableOfContents.Section tocSec = getTocSection(dex); if (!tocSec.exists()) { return EMPTY_ENTRY_ARRAY; } Dex.Section dexSec = dex.openSection(tocSec); int itemCount = tocSec.size; List<AbstractMap.SimpleEntry<Integer, T>> result = new ArrayList<>(itemCount); if (isOldDex) { for (int i = 0; i < itemCount; ++i) { T nextItem = nextItem(dexSec); T adjustedItem = adjustItem(oldToPatchedIndexMap, nextItem); //按照patchedDex的顺序来调整 result.add(new AbstractMap.SimpleEntry<>(i, adjustedItem)); //加入调整后的item } } else { int i = 0; while (i < itemCount) { T nextItem = nextItem(dexSec); int indexBeforeSkip = i; int offsetBeforeSkip = getItemOffsetOrIndex(indexBeforeSkip, nextItem); int indexAfterSkip = indexBeforeSkip; //跳过不需要 考虑的item while (indexAfterSkip < itemCount && shouldSkipInNewDex(nextItem)) { if (indexAfterSkip + 1 >= itemCount) { // after skipping last item, nextItem will be null. nextItem = null; } else { nextItem = nextItem(dexSec); } ++indexAfterSkip; } if (nextItem != null) { int offsetAfterSkip = getItemOffsetOrIndex(indexAfterSkip, nextItem); //adjustItem(selfIndexMapForSkip, nextItem) 按照跳过的关系 拿到item,然后根据newToPatched的关系来调整newDex中item的顺序 T adjustedItem = adjustItem(newToPatchedIndexMap, adjustItem(selfIndexMapForSkip, nextItem)); int currentOutIndex = result.size(); result.add(new AbstractMap.SimpleEntry<>(currentOutIndex, adjustedItem)); updateIndexOrOffset(selfIndexMapForSkip, indexBeforeSkip, offsetBeforeSkip, indexAfterSkip, offsetAfterSkip); } i = indexAfterSkip; ++i; } } return result.toArray(new AbstractMap.SimpleEntry[0]); }
对比两个调整后dex的item
根据比较的结果把操作的步骤放到patchOperationList中,
int oldCursor = 0; int newCursor = 0; while (oldCursor < this.oldItemCount || newCursor < this.newItemCount) { if (oldCursor >= this.oldItemCount) { // rest item are all newItem. //剩余的 全部是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. // 剩下的全是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) { //如果结果小于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) { //结果大于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; } } }
patch操作的排序优化
为了让patch操作更加优雅和简洁,需要按照一定顺序进行排序,主要是为了后面的合并步骤,
//对patch的操作进行排序优化 Collections.sort(this.patchOperationList, comparatorForPatchOperationOpt);
add和delete进行合并操作
add和delete如果发生在同一个位置,可以合并为replace操作,
Iterator<PatchOperation<T>> patchOperationIt = this.patchOperationList.iterator(); PatchOperation<T> prevPatchOperation = null; while (patchOperationIt.hasNext()) { PatchOperation<T> patchOperation = patchOperationIt.next(); //对add和del进行replace合并操作 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; } }
记录patch过程中的操作
// 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; } } }
模拟patch过程
每次section进行上面的步骤后,都要模拟一次patch过程,主要为了计算patched之后的对应关系和section的size。
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); }
六、addTestDex
这里会加入test.dex,不进行详述。主要是能够更好地标识这一过程已经完成。
- 热更新Tinker研究(七):Dex的patch文件生成
- 热更新Tinker研究(九):Dex文件的patch
- 热更新Tinker研究(十):Res文件的patch
- 热更新Tinker研究(十一):so文件的patch
- 热更新Tinker研究(八):res和so的patch文件生成
- 热更新Tinker研究(二):结合源码学习Dex格式
- 热更新Tinker研究(五):Application的隔离
- 热更新Tinker 的研究与集成
- 热更新Tinker研究(四):TinkerLoader
- 热更新Tinker研究(六):TinkerPatchPlugin
- 热更新Tinker研究(一):运行tinker-sample-android
- 热更新Tinker研究(三):加载补丁
- Android热更新(Tinker)
- tinker热修复--集成tinker patch 详解
- Android热更新开源项目Tinker源码解析系列之一:Dex热更新
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
- 关于 Android中的插件化开发,dex分包,热修复(Tinker)的思考(一)
- 关于 Android中的插件化开发,dex分包,热修复(Tinker)的思考(二)
- ubuntu 工作中常用的终端命令
- service层的意义
- java bug
- 收藏文档链接
- ubuntu16.04 编译安装kubernetes1.6.1
- 热更新Tinker研究(七):Dex的patch文件生成
- 关于项目感叹号及报错调试的一点心得
- Junit4出现java.lang.Exception:No tests no found macthing
- AOP
- k-均值:一种基于型心的技术
- 动态添加权限
- SEO之外链发布方法
- Servlet+JSP 实现验证码
- 二叉树的遍历