APK construction principle from shallow to deep

Overview

The APK building process involves many tools and processes for converting a project into an Android application package (APK). The build process is very flexible, so it’s helpful to understand some of its underlying working principles.

The detailed APK construction process is a bit more complicated. This article will be divided into four steps, from the shallower to the deeper, with readers in-depth analysis of the construction process.

  • The first step: see the build
  • Step 2: Revisit the construction
  • The third step: in-depth construction
  • Step 4: Abandon the build

First sight build

First of all " seeing the construction ", in this step we can ignore the details of the construction first, and first have a general understanding of the main process.

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

1, the entire construct is divided into two main operations: compiling (the Compile), packing (APK Package)

2. Compiler : Compiler compiles source code, AIDL files, resource files, and dependent packages, and finally generates 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 file to package into the final APK file.

Revisit construction

In the previous step " First See Construction ", we have a general understanding of the APK packaging process, let's go a little more in detail.

( Square : indicates the file, ellipse : indicates the tool and operation)

The picture above shows a more detailed construction process. Bounded by a broken line, the first part describes the compiling process , described in the latter part of the packaging process .

The following specific analysis of the build flow is divided into seven steps (compilation 1-4, packaging 5-7):

    1. Use the __aapt__ tool to compile the res/file and generate the compiled binary resource file (.ap_ file) and R.java file. (Currently, the new version uses the aapt2 tool, and R.java has also been replaced with R.jar)

    2. Use the __aidl__ tool to generate the corresponding Java interface file based on the aidl file

    3. Use the __Java Compiler__ tool, Java Compiler (commonly known as javac) to compile R.java, the code in the project, and the Aidl interface file into a .class file.

    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 (for example: so files) 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 volume of the signed apk file (this step is only available for the v1 signature, and the v2 signed apk will be damaged after zipalign).

Let's rest for 3 minutes and digest.

Build in depth

Ready to go deep into the construction process, the picture seems complicated, but there are picture-reading skills:

We can interpret this flowchart by referring to the pictures shown in " Revisiting Construction ".

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

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

Take aapt as an example, in-depth analysis of a more detailed construction process:

    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 internal projects, android.jar, and arr. These merged files are used as input files. After appt is compiled, the compiled resource files, R.java files, and aapt is also responsible for merging various obfuscated files.

    In addition, there is a proguard (obfuscation) between javac and dex operations. Proguard reads the .class file, generates the obfuscated .class file, and gives it to dex (or d8).

Give up building

Very powerful, your understanding of the Android build process has surpassed most developers (I guess :-)).

But it's still a bit far from practice. Think about how gradle implements the above-mentioned build process when we click to run?
When we run the assembleReleasecommand to pack, huh? What is this string? Facing the above analysis process, you will know what it is.

Interpretation version:

//以下两个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

It is said that " abandon construction ", why haven't you given up yet? We learned that the build process, now give up automated build apk, use the manual to build apk. Proceed as follows:

    1. Compile the res resource files in the project (except assets), generate corresponding binary resource files, and pack these binary files into the res.zip file

$aapt2 compile -o build/res.zip --dir ./app/src/main/res

    2. Connect the res.zip file with the SDK to generate .ap_ files (including compiled resource files, manifest files, and arsc files) and R.java files,

$ 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 the java file into a .class file

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

Some students may have questions. What is the relationship between aapt, aidl, javac, dex, apkbuilder Jarsigner, zipalign and the Gradle plug-in task?

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.

to sum up

At this point, the APK construction process is basically analyzed, and readers can further understand the specific operations of gradle plugin packaging according to the source code of the gradle plugin.

You can write the following code in the build.gradle file, output the class corresponding to each Task, and then view 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"
    ...
}

(For the mistakes and deficiencies in the knowledge reserve article, please comment and leave a message, discuss together, and make progress together:-)

Guess you like

Origin blog.csdn.net/weixin_36570478/article/details/109018059