Manually build the APK process

Ignore the details in the construction first, and first have a general understanding of the main process.

As shown in the figure below (square: indicates file, diamond: indicates operation)

  1. The whole build is divided into two main operations: compilation (Compile), packaging (APK Package)
  2. Compilation: The compiler (Compiler) compiles source code, AIDL files, resource files, and dependency packages to finally generate Dex files and compiled resource files.
  3. Packaging: The packager (APK Packager) uses the signature file (KeyStore), the Dex file generated in the previous compilation process, and the compiled resource files to package the final APK file.

(Square: indicates files, ellipse: indicates tools and operations)
The above picture shows a more detailed construction process. Bounded by the dotted line, the first half describes the compilation process, and the second half describes the packaging process.

The following is a detailed analysis of the construction flow, which is divided into seven steps (including compiling 1-4 and packaging 5-7):

  1. Use the aapt tool to compile res/ files to generate compiled binary resource files (.ap_ files) and R.java files. (Currently the new version uses the aapt2 tool, and R.java is also replaced by R.jar)
  2. Use the aidl tool to generate the corresponding Java interface file according to the aidl file
  3. Using the *Java Compiler tool, Java Compiler (commonly known as javac) compiles R.java, code in the project, and Aidl interface files into .class files.
  4. Use the dex tool to compile the .class file generated in the previous step and the .class in the third-party dependencies into a .dex file. (Currently the new version uses the d8 tool)
  5. Use the apkbuilder tool to compress the compiled resources (.ap_ files), dex files and other resource files (such as so files, asset files, etc.) into an .apk file.
  6. Use the Jarsigner tool to read the signature file, sign the apk file generated in the previous step, and generate a signed apk file.
  7. Use the zipalign tool to optimize the size of the signed apk file (this step is only available for v1 signatures, and the signature of v2 signed apk will be destroyed after zipalign).
    insert image description here
    This flowchart can be interpreted with reference to the picture shown in the second figure.

Use the ellipse (tools and operations) as a clue to understand this diagram according to the construction process mentioned earlier. Are you familiar with aapt, aidl, javac, dex, apkbuilder Jarsigner, zipalign? The big block on the left is about Render Script and ndk compilation, we can ignore it temporarily (I believe you can analyze it yourself if you don’t ignore it after reading this article), only know that it provides .so files and some Java files.

First, look at the lower left corner. There are three types of ellipse operations: provided by the Gradle plugin (completed by the task), provided by the Build tool, and provided by the JDK

Taking aapt as an example, analyze the more detailed construction process in depth:

Before aapt, various Merge tasks in the Gradle plug-in will merge Manifest, Res, and Assets. These merged resources may come from third parties such as the project internals, android.jar, and arr. These merged files are used as input files. After compiling by appt, the compiled resource files and R.java files are also responsible for merging and generating various confusing files.

In addition, there is an additional proguard (confusion) between javac and dex operations. Proguard reads the .class file, generates a confused .class file, and hands it to dex (or d8).

How does gradle implement the above build process when we click to run? When we run the assembleRelease command to package, huh? What is this string? Facing the process analyzed above, you will know what it is.

//以下两个task是预编译工作,暂不关心
> Task :app:preBuild UP-TO-DATE
> Task :app:preReleaseBuild UP-TO-DATE

//aidl编译
> Task :app:compileReleaseAidl NO-SOURCE

//生成BuildConfig文件
> Task :app:generateReleaseBuildConfig

//编译Renderscrip,暂不关心(感兴趣的可以去探究Renderscrip)
> Task :app:compileReleaseRenderscript NO-SOURCE
//*
> Task :app:javaPreCompileRelease

//生成资源文件并合并
> Task :app:generateReleaseResValues
> Task :app:generateReleaseResources
> Task :app:createReleaseCompatibleScreenManifests
> Task :app:extractDeepLinksRelease
> Task :app:processReleaseManifest
> Task :app:prepareLintJar UP-TO-DATE
> Task :app:checkReleaseDuplicateClasses
> Task :app:desugarReleaseFileDependencies
> Task :app:mergeReleaseResources

//产生build/intermediates/compile_and_runtime_not_namespaced_r_class_jar/release/R.jar文件
> Task :app:processReleaseResources

//javac将java编译成Class文件
> Task :app:compileReleaseJavaWithJavac

//将资源文件编译并生成resource.arsc文件,并放入.ap_文件中(./app/build/intermediates/processed_res/release/out/resources-release.ap_)
> Task :app:compileReleaseSources

> Task :app:lintVitalRelease

//dex工具将.class文件编程传.dex文件
> Task :app:dexBuilderRelease

//合并非res/的资源文件及assets文件
> Task :app:mergeExtDexRelease
> Task :app:mergeReleaseShaders
> Task :app:compileReleaseShaders NO-SOURCE
> Task :app:generateReleaseAssets UP-TO-DATE
> Task :app:mergeReleaseAssets
> Task :app:processReleaseJavaRes NO-SOURCE
> Task :app:collectReleaseDependencies
> Task :app:sdkReleaseDependencyData
> Task :app:mergeReleaseJniLibFolders
> Task :app:mergeReleaseNativeLibs
> Task :app:stripReleaseDebugSymbols NO-SOURCE
> Task :app:mergeReleaseJavaResource

//合并.dex文件
> Task :app:mergeDexRelease

//将.dex文件、.ap_打包进以及非res资源文件打包进.apk文件中并签名
> Task :app:packageRelease

使用zipalign对apk进行体积优化
> Task :app:assembleRelease

After understanding the construction process, now give up the automatic construction of apk and use manual construction of apk. Proceed as follows:

  1. Compile the res resource files (except assets) in the project, generate corresponding binary resource files, and package these binary files into the res.zip file$aapt2 compile -o build/res.zip --dir ./app/src/main/res
  2. Connect res.zip file with sdk to generate .ap_ file (including compiled resource file, manifest file, arsc file) and R.java file,$aapt2 link build/res.zip -I $ANDROID_HOME/platforms/android-21/android.jar --java build --manifest ./app/src/main/AndroidManifest.xml -o build/app-debug.ap_
  3. Compile java files into .class files$javac -d build -cp $ANDROID_HOME/platforms/android-21/android.jar com/*/.java
  4. Compile the .class file generated in the previous step into a .dex file$d8 --output build/ --lib $ANDROID_HOME/platforms/android-21/android.jar build/com/example/application/*.class
  5. Use the zip command to package the .ap_resource file in step (2) and the .dex file in step (4) into an unsigned apk$zip -j build/app-debug.ap_ build/classes.dex
  6. Modify .ap_ to .apk, and use apksigner to sign the apk$apksign -ks ~/.android/debug.keystore build/app-debug.apk

What is the relationship between aapt, aidl, javac, dex, apkbuilder Jarsigner, zipalign and the task of the Gradle plugin?

Answer: The automatic build process of the Gradle plugin is to directly or indirectly call the tools used in the manual packaging process in the Task.

At this point, the basic analysis of the APK construction process is completed, and readers can further understand the specific operation of the gradle plug-in packaging according to the source code of the gradle plug-in.

You can write the following code in the build.gradle file to output the class corresponding to each Task, and then check the specific work of the Task:

//build.gradle
gradle.taskGraph.whenReady {
    
    
    it.allTasks.each {
    
     task ->
        println("Task Name : ${task.name}")
        task.dependsOn.each{
    
     t->
            println "-----${t.class}"
        }
        //def outputFileStr = task.outputs.files.getAsPath();
     //def inputFileStr = task.inputs.files.getAsPath()
    }
}

dependencies {
    
    
    ...
    testImplementation "com.android.tools.build:gradle:4.0.0"
    ...
}

Why do I occasionally encounter third-party .so files missing when running, which was fine just now, but suddenly fails again?

//运行如下两条命令即可
$rm -rf ./app/build/intermediates/merged_native_libs
$rm -rf ~/.gradle/caches/build-cache-1

Explanation: When relying on a third-party package, the third-party package is first downloaded to ~/.gradle/caches/modules-2this cache directory, then decompressed into ~/.gradle/caches/transforms-2/files-2.1a file , and finally the decompressed file is merged with the files in the project, and ~/.gradle/caches/build-cache-1the files are backed up in the file after the merge (compressed backup in the form of a package for future use).
.soIt will be merged into ./app/build/intermediates/merged_native_libsthe directory . .soMost of the loss is due to a merge error, so delete the merged file and backup file, and then re-execute the merged task in gradle.

Guess you like

Origin blog.csdn.net/rd_w_csdn/article/details/120185179