Android Gradle Plugin, AGP for short, has long wanted to study the Android APK packaging process, after all, APK package volume optimization is one of the pre-knowledge.
However, my knowledge reserve at that time was severely insufficient, so it was really difficult to study hard. After studying Gradle for two weeks, I felt that I should have the strength to fight, so here comes this section!
More content, it is recommended to collect first, there is time to slowly fine ~
0x1. Three APK packaging flowcharts circulating on the Internet
The Android website has a new packaging flowchart (left) that is more abstract than the old one (right) and hides a lot of details:
A more detailed image was also found at the Android Studio Project Site:
If only satisfied with a write basic business Android development son, understand enough, but if you want to have deeper attainments, or suggest to learn.
For example, in an interview (my own imagination ~) :
- Interviewer: Did YOU do APK volume optimization on your resume? Talk about what aspects of optimization have been done:
- You: Start with the resource headers, turn PNG images into WebP, and use AndResGuard to confuse the resources;
- Interviewer: Write a webP plugin using Gradle.
- You: No, I usually right-click to convert directly…
- Interviewer: Tell me the general principle of AndResGuard;
- You:
This section takes a look at the AGP build process and a brief look at the APK packaging process
Tips: There are so many Tasks that it is impossible to read the source code one by one. There are differences between different versions of plug-ins, so it is better to teach them how to do it. The purpose of this article is to let the reader know how to find the source code when they encounter a problem.
How to view the source code of the plug-in
Research object is AGP source code, so to do a source code, there are several methods as follows:
Download the full source code
If you have enough disk space, you can download the source code of Android Gradle Plugin to the local via the repo (looks like 30 gb) :
# the latest source only 3.4.0 repo init -u https://android.googlesource.com/platform/manifest - b gradle_3. 4.0 repo syncCopy the code
2. Download some source code
Of course, if you do not need to compile, you can directly go to the corresponding source package, the following address: build-system
Click TGZ to download:
Then use a Code viewing tool like VS Code to view it
3. Take the easy way out (recommended)
Add the following dependencies to build.gradle in the app hierarchy:
Implementation 'com. Android. Tools. Build: gradle: 3.4.0'Copy the code
Under Build, go to External Libraries on the left:
0x3, some additions before reading the source code
Before reading the source code, it is recommended to review the three articles I wrote before, and add some postures:
Gradle Plugin has three types of tasks: normal Task, incremental Task and Transform.
Tasks typically inherit DefaultTask or IncrementalTask, and the @taskAction annotated method is what the Task does.
A class that inherits from IncrementalTask is an IncrementalTask. This increment is relative to full, which refers to the first compilation process after clean is called and the second compilation process after code or resources are modified. A few key approaches:
public abstract class IncrementalTask extends BaseTask {
// Whether increments are required. The default is false
@Internal protected boolean isIncremental(a) {}// Subclasses are required to implement tasks performed at full scale
protected abstract void doFullTaskAction(a) throws Exception;
// By default, nothing is executed, and the parameter is the file that was modified during the increment
protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws Exception{}@TaskAction
void taskAction(IncrementalTaskInputs inputs) throws Exception {
DoIncrementalTaskAction; otherwise doFullTaskAction
ο½
// Get the modified file
private Map<File, FileStatus> getChangedInputs(IncrementalTaskInputs inputs) {}}Copy the code
As for Transform, Android officially provides developers with a set of APIS to modify.class files during **.class β.dex transformations. Just pay attention to the implementation of the Transform () method.
0x4. Execute Task chain for Gradle Assemble
We often package APK with the following command:
gradlew assemble
Copy the code
To see what tasks are involved in packaging once, type the following command (Linux, MAC use./gradlew) :
gradlew assemble --console=plain
Copy the code
The output results and key points are described as follows:
:app:preDebugBuild β empty task :app:preDebugBuild β empty task, Anchor: app: compileDebugAidl NO - SOURCE - > processing AIDL: app: checkDebugManifest to check whether the Manifest: app: compileDebugRenderscript NO - the SOURCE - handling renderscript: app: generateDebugBuildConfig - > generate BuildConfig. Java: app: mainApkListPersistenceDebug - generation App - list. Gson: app: generateDebugResValues - > generate resvalue, generated. XML: : app generateDebugResources task and empty, Anchor: app: mergeDebugResources - > merge resource files: app: createDebugCompatibleScreenManifests to manifest file generated in the compatible - screens, Specified screen adaptation: app: processDebugManifest to merge the manifest. The XML file: app: processDebugResources - aapt packaging resources: app: compileDebugKotlin - > Compile Kotlin file: app: prepareLintJar the UP - TO - DATE - > copy lint jar packet TO the specified location: app: generateDebugSources task and empty, Anchor: app: javaPreCompileDebug - > generate annotationProcessors json file: app: compileDebugJavaWithJavac to compile the Java file :app:compileDebugNdk β compile NDK :app:compileDebugSources β empty task, Anchor point :app:mergeDebugShaders β merge shader files :app:compileDebugShaders β compile Shaders :app:generateDebugAssets β Empty task, Anchor: app: mergeDebugAssets - > merge assests file: app: validateSigningDebug to verify the signature: app: signingConfigWriterDebug - > Write SigningConfig information: app: checkDebugDuplicateClasses - inspection repeat class: app: transformClassesWithDexBuilderForDebug - class packaged into dex App: dex transformDexArchiveWithExternalLibsDexMergerForDebug - packaged third-party libraries: app: transformDexArchiveWithDexMergerForDebug - > Packaging of the final dex: app: mergeDebugJniLibFolders - > merge jni.lib file: app: jnilibs transformNativeLibsWithMergeJniLibsForDebug - merger App: transformNativeLibsWithStripDebugSymbolForDebug - > remove the debug symbols in the native lib: app: processDebugJavaRes NO - SOURCE to deal with Java Res: app: transformResourcesWithMergeJavaResForDebug - > merge Java res: app: packageDebug - packaged apk: app: assembleDebug task and empty, Anchor: app: confused extractProguardFiles - generated file< task > < task > < task > < task > < task
Copy the code
You can also view it directly from the Build window. Double-click the Task assemble in the Gradle window on the right and look at this window:
You can also see the execution time of each Task, which is good, but not with the specific content of each Task, but with the AGP build process ~
0x5 AGP build process
As mentioned in the previous section, each Gradle plugin will be configured with a file named.properties. In this file, write the plugin implementation class and search globally to find the following file:
Open:
Point to the: AppPlugin class, and follow:
AbstractAppPlugin β BasePlugin (BasePlugin) AbstractAppPlugin β BasePlugin (BasePlugin) AbstractAppPlugin β BasePlugin
1) BasePlugin
Okay, overridden in BasePluginapply()
Method, which calls two functions.basePluginApply()
Some checks are performed, followed by initialization and configuration of the plug-in, and another function, pluginSpecificApply(), is an empty implementation, followed by the process of configuring the project, configuring the extension, and creating the Tasks.
β‘ configureProject() β configureProject
Create a DataBindingBuilder instance, force the use of no less than the minimum plug-in version currently supported, apply the Java plug-in, create a buildCache instance if the buildCache option is enabled, and add a callback to perform the reclaims-related operations after all projects have been executed.
β’ configureExtension() β Configure DSL extension
Complete the following tasks:
- Create Android DSL in Build. gradle;
- Create instance VariantFactory, TaskManager, VariantManager;
- β’ Register callbacks for new/removed configurations, including signingConfig, buildType, and productFlavor;
- Create default debug signature, debug, and release buildType;
CreateTasks () β createTasks
With the createAndroidTask () :
With the createAndroidTasks () :
Note: under the traverse all the variantScope, then call createTasksForVariantData () to create variation data corresponding to the Tasks:
To: createTasksForVariantScope () :
Abstract method. To see where this method is implemented, search: extends TaskManager
Finally, the: ApplicationTaskManager class is located
CreateAnchorTasks () createAnchorTasks() createAnchorTasks()
To: createVariantPreBuildTask ()
2333, in line with the APK packaged Task chain above, the AGP plug-in build process follows here, and then learn about APK packaged Task.
0x6, Apk packaging process
Global search “XXX “, “yyy” can quickly locate the corresponding Task class, such as “compile”, “Aidl”, or search the whole Task, and then delete delete matching.
1. compileDebugAidl
Process description: convert. Aidl files into Java interface files that can be processed by the compiler using the AIDL tool: aidlcompile.java β aidlprocessor.java β call()
2. checkDebugManifest
Procedure description: check whether the androidmanifest.xml file has relevant code: checkmanifest.java
3. compileDebugRenderscript
Process description: process Renderscript files (.rs) related code: renderScriptcompile.java
4. generateDebugBuildConfig
Process description: Generate buildconfig. Java file related code: GenerateBuildConfig.java
5. mainApkListPersistenceDebug
Process description: persistent APK data to the APK – list. Gson in relevant code: MainApkListPersistence. Kt
6. generateDebugResValues
Procedure Description: Walk through the XML file in the values directory under res to generate the generated. XML code of the resValues file: GenerateResValues. Java β generate() β resValueGenerator.java
7. mergeDebugResources
Process description: use AAPT2 to merge resource files MergeResources. DoFullTaskAction () – > ResourceMerger. MergeData () – > MergedResourceWriter. The end () – > MResourceCompiler. SubmitCompile () – > AaptV2CommandBuilder. MakeCompileCommand ()
Core source code parsing:
Incremental() method has been implemented, returning true to indicate that incremental compilation is supported, following the full compilation method doFullTaskAction()
ResourcePreprocessor preprocessor = getPreprocessor();
List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor)
Copy the code
Then go down:
Continue to:
Merger. MergeData () β Resourcemerger.mergeData () β Datamerger.mergeData ()
The MergedResourceWriter method is called with the following addItem() :
Each file creates an instance of the corresponding compileresourcerequests and adds it to the mCompileResourceRequests, which is a ConcurrentLinkedQueue, and the resource is finally processed at the end() method:
Final call AaptV2CommandBuilder. MakeCompileCommand () method to generate aapt2 command to deal with resources.
Tips: Plugins that convert images to WebP format are usually processed before this Task
8. createDebugCompatibleScreenManifests
Course description: the manifest file generated in the compatible – screens, used for screen adaptation related code: CompatibleScreensManifest. Kt
9. processDebugManifest
Process description: merge AndroidManifest. XML file related code: ProcessApplicationManifest. Java, ProcessLibraryManifest. Java
10. processDebugResources
Process description: call AAPT2 link to package resources and generate r.java file code: taskManager.java β createProcessResTask()
11. compileDebugKotlin
Compile Kotlin file as bytecode Probably in the kotlin plugin source code
12. prepareLintJar
Process description: copy lint jar packet to the specified location related code: PrepareLintJar. Java
13. avaPreCompileDebug
Process description: generate annotationProcessors. Json file related code: JavaPreCompileTask. Java
14. ompileDebugJavaWithJavac
Process description: Java file related code: AndroidJavaCompile. Java
15. compileDebugNdk
Process description: compile NDK related code: ndkcompile.java
15. mergeDebugShaders
Process description: merge Renderscript file. (rs) code: MergeSourceSetFolders. Java
16. compileDebugShaders
Process description: compile Renderscript file (.rs) related code: Shadercompile.java
17. mergeDebugAssets
Process description: merge assets file related code: MergeSourceSetFolders. Java
18. validateSigningDebug
Procedure description: verify the signature related code: ValidateSigningTask. Kt additional information: check whether the keystore file exists in the current Variant signature configuration. If the default keystore is debug keystore, the corresponding keystore will be created if it does not exist.
19. signingConfigWriterDebug
Course description: SigningConfig information related code: SigningConfigWriterTask. Kt
20. checkDebugDuplicateClasses
Process description: check the duplicate class related code: CheckDuplicateClassesTask. Kt additional information: check whether project external dependencies contains no repeat classes, packaged in dex detection error when not very friendly, so the introduction of the Task to fail fast.
21. transformClassesWithDexBuilderForDebug
Process description: will the class packaged into dex related code: DexArchiveBuilderTransform. Java
Core code parsing:
If you go to the transform() method, you can see that there are two types of class processing, the class in the directory and the class in the.jar:
With the processJarInput () :
Continue with: convertJarToDexArchive()
For both classes, we end up going to convertToDexArchive(), where launchProcessing() is called:
DexArchiveBuilder here. The convert () is called dx or d8 to play dex, along with the assignment:
22. transformDexArchiveWithExternalLibsDexMergerForDebug
Process description: third-party libraries packaging dex related code: ExternalLibsMergerTransform. Kt core code parsing:
Same as transform() :
Create a DexMergerTransformCallable instance, then the call () method:
Simple, is to adjust dx or D8 to generate the above dependencies of the library dex into a dex.
23. transformDexArchiveWithDexMergerForDebug
Process description: packaged final dex related code: DexMergerTransform. The transform () – > mergeDex () core code parsing:
With the submitForMerging () :
Also created a DexMergerTransformCallable instance, remaining logical ditto ~
24. mergeDebugJniLibFolders
Process description: merge the jni.lib file related code: MergeSourceSetFolders. Java
25. transformNativeLibsWithMergeJniLibsForDebug
Process description: merge jnilibs relevant code: MergeJavaResourcesTransform. Java
26. transformNativeLibsWithStripDebugSymbolForDebug
Process description: remove the native debug symbols related code in the lib: StripDebugSymbolTransform. Java
27. processDebugJavaRes
Process description: Java res related code: MergeJavaResourcesTransform. Java
28. transformResourcesWithMergeJavaResForDebug
Process description: merge the Java res related code: MergeJavaResourcesTransform. Java
29. packageDebug
Process description: packaged APK relevant code: PackageApplication. Java – PackageAndroidArtifact. DoTask ()
The core code is as follows:
All of the updateXxx() methods above call IncrementalPackager β updateFiles()
Finally, mApkCreator. WriteZip is called to write the above content to APK.
30. extractProguardFiles
Process description: generate confusion files related code: ExtractProguardFiles. Java
Added: Anchor Task β empty Task
The Tasks above filter the anchor Task. What is the anchor Task? A: An empty Task is used to indicate a certain state.
TaskManager β MAIN_PREBUILD: TaskManager β MAIN_PREBUILD
Along with the reference: createTasksBeforeEvaluate () :
A Task named **MAIN_PREBUILD** is registered, but no closure (Task content) is passed, that is, an empty Task.
summary
The above is all content of this section, after reading it seems to understand what, but can not say what you understand, it doesn’t matter, after all, a bit theoretical, for the back of Gradle more in-depth learning and application to do it, no need to worry, if you have any questions or in the article wrong place welcome to comment area point out, thank you ~
Hey, what time is it three o ‘clock?
References:
-
Android Gradle Plugin: Android Gradle Plugin: Android Gradle Plugin: Android Gradle Plugin
-
γ soul 7 ask γ deep exploration Gradle automatic building technology (five, Gradle plug-in architecture implementation principle analysis — next)
-
Gradle Learning Notes – Some tasks used during compilation
-
AAPT2
-
Android Gradle (Android gradle)