Defining difference plug-ins
- Gradle directory and build directory, build. Gradle directory and build directory, build. Gradle file contents are as follows: And generate the corresponding plug-in, such as apply Plugin: “groovy”, our new file bsdiffplugin.groovy will take effect
buildscript { repositories { google() jcenter() mavenCentral() } dependencies { classpath 'com. Android. Tools. Build: gradle: 4.1.2'}} / / identify groovy plugin apply plugin: "groovy" apply the plugin: "java" repositories { mavenCentral() } dependencies { }Copy the code
- 2. Create a Groovy directory and define bsDiffplugin.groovy
The plugin is paired with the Task, the plugin is responsible for building the task and initializing its parameters, and the task is responsible for the actual task execution.
Class BsDiffPlugin Plugin<Project>{@override void apply(Project Project) {project.afterevaluate { Will not execute, /gradlew bsDiff project.task(type:BsDiffTask, "bsDiffWrapper"){ inputs.files "${project.rootDir.absolutePath}/temp/testold.txt","${project.rootDir.absolutePath}/temp/testnew.txt" outputs.files "${project.rootDir.absolutePath}/temp/testdiff.patch" } } } }Copy the code
When we add it in build.gralde under app
apply plugin: BsDiffPlugin
Copy the code
In gradle configuration, the apply method of BsDiffPlugin is executed, while the task defined in BsDiffTask is not executed, it is simply initialized
Task (type:BsDiffTask, “bsDiffWrapper”) the second parameter changes the name of the task
- 3. Perform tasks
There are two ways to execute a task
./gradlew tasks
Copy the code
Make a list of tasks and execute
./gradlew bsDiffWrapper
Copy the code
Or check a default build task execution
project.getTasksByName("assembleDebug",false).getAt(0).finalizedBy 'bsDiffWrapper'
project.getTasksByName("bsDiffWrapper",false).getAt(0).finalizedBy 'bsPatchWrapper'
Copy the code
Define the id for the plug-in
- 1. Build resources/META_INF/gradle-plugins under buildSrc /main
- 2. Create two new properties files whose names are the ids we will rely on
com.hch.plugin.bsDiff.properties com.hch.plugin.bsPatch.properties
- 3. Add a line to the file that represents the plug-in’s implementation classpath
implementation-class=com.hch.plugin.BsPatchPlugin
Copy the code
- 4. Use id dependencies in gradle where you need them, such as build.gradle in your app. Plugins must be placed at the beginning of the file and must not be preceded by other scripts
plugins{
id 'com.hch.plugin.bsDiff'
id 'com.hch.plugin.bsPatch'
id 'com.android.application'
}
Copy the code
Dex file format analysis
Format viewing tool 010Editor
The number of methods cannot exceed 65535
In dex, the description id of the class pointed to by the method was USHORT and the length was 65535. Therefore, if the number of classes exceeded 65535, too much classes would be prompted.
Defects and improvements of bsDiff/dexDiff comparison algorithm
Check the method of Tinker comparison algorithm and copy the implementation class by relying on Tinker’s Patch plug-in
CompileOnly "com. Tencent. Tinker: tinker - patch - gradle - plugin: 1.9.1." "Copy the code
The structure of dex
Dex is structured data that contains headers, maps, and data sections.
The header contains:
- Magic string [dex. 035.],
- Checksum Checksum data
- Other section types and length descriptions, such as string, ID, prototype,method,class initial position, length, etc. String, ID, prototype,method,class each structure is a structured data
Compared with header, MAplist more comprehensively describes the entire block information of DEX
Datasection Information about the actual contents of each Secion
Dex algorithm implementation
Therefore, the basic flow of the comparison algorithm can be determined as follows:
- Dex file Reads and converts dex memory objects
- For structural comparison of 15 data structures in DEX, the approximate implementation process is as follows: Read item of each structured list and sort it, generate oldList and newList, circulate cursor of list according to current cursor, obtain oldItem and newItem respectively, and compare their value:
-
- If <0, the oldItem is considered to be deleted and recorded as patchoperation. OP_DEL. The oldItem index is recorded in the PatchOperation object and added to the patchOperationList.
-
- If the value is greater than 0, the newItem is considered to be new and recorded as patchoperation. OP_ADD. The newItem index and value are added to the PatchOperation object and added to the patchOperationList.
-
- If =0, PatchOperation will not be generated.
- Get the result map, items to be deleted from oldDex, items to be added to newDex, and items to be replaced
// 2. Collect patch operations while (oldCursor < enclosing oldItemCount | | newCursor < enclosing newItemCount) {if (oldCursor > = this. OldItemCount) { // Rest item are all newitem. while (newCursor < this.newItemCount) {} else if (newCursor >= NewItemCount) {// Rest item are all olditem. while (oldCursor < oldItemCount) {// DEL the rest oldItem}} else {}}Copy the code
Dex Diff’s flaws
We can use array + linked list to store the key values (int) generated according to the hashcode of the item. We can compare the key values with o(1) complexity to get the comparison result