This is the 10th article in the Android Training Manual series. If you haven’t read the previous articles, please click here to check them out
Preliminary knowledge
- Understand basic Android development
How far can I go after reading this article
- Understand the key process of Android APK packaging
- Understand the principle of multi-channel packaging
- Understand Multiple APK, Split APKs, App Bundle derivative packaging methods
Prepare before reading
- Clone CommonTec project, where Simpleapk is the example used in this article.
The article gives an overview of
Most of our development is now done in Android Studio. In AS, we just hit the Run button and AS will automatically package Apk and install it on the device and Run. For us, the packaging process is a black box, all we know is Sources -> AS Compiler -> APK. In this article, we will analyze the packaging process in the middle and some problems related to packaging.
Let’s take a look at the internal structure of APK.
Android APK package structure
Let’s look at the structure of a normal APK. After an APK is packaged, there are usually several directories for storing different files. Assets native resource files are not compressed or processed by classes.dex Java code is converted to a class file using javac, and then converted to a dex file using dx. If there were more than one dex file, it would be named like this: class.dex classes2.dex classes3.dex… Class information is saved there. Lib/stores native library. So files, which are divided into different directories based on the CPU model, such as ARM, x86, etc. Res/holds the processed binary resource file. Resources.arsc holds a mapping of resource ID names and their corresponding values/paths. Meta-inf/is used to verify the APK signature, which has three important files manifest.mt, cert. SF, cert.rsa. Manifest.mf holds a summary of all files, which reads as follows:
Manifest-version: 1.0 built-in: Generated- By -ADT Created-By: Android Gradle 3.4.0 Name: AndroidManifest.xml SHA-256-Digest: QxJh66y6ssDSNFgZSlf5jIWXfRdWnqL1c3BSwSDUYLQ= Name: META-INF/android.arch.core_runtime.version SHA-256-Digest: zFL2eISLgUNzdXtGA4O/YZYOSUPCA3Na3eCjULPlCYk=Copy the code
Cert.sf saves a summary of each message in manifest.mf, which reads in part as follows:
Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA-256-Digest-Manifest: j8YGFgHsujCHud09pT6Igh21XQKSnG+Gqy8VUE55u+g=
X-Android-APK-Signed: 2
Name: AndroidManifest.xml
SHA-256-Digest: qLofC3g32qJ5LmbjO/qeccx2Ie/PPpWSEPBIUPrlKlY=
Name: META-INF/android.arch.core_runtime.version
SHA-256-Digest: I65bgli5vdqHKel7MD74YlSuuyCR/5NDrXr2kf5FigA=
Copy the code
Cert. RSA contains the signature of the cert. SF file and the certificate used for the signature.
Androidmanifest.xml is a file that everyone is familiar with, the global configuration file, but here is the compiled binary file.
Ii. APK packaging process
2.1 Original packaging process
We have seen a complete APK structure above, and the process of APK packaging is actually the process of generating the above files. Here is a flow chart that is widely circulated on the Internet.
There are mainly the following steps:
- Compile the resource files using AAPT/AAPT2 to generate resources. Arsc and R.java
- Use AIDL to process AIDL files and generate Java files
- Compile Java files using JAVAC to generate classes files
- Use DX/D8 to process the class file and generate the dex file required in the end
- Use Android NDK to process native code generation.so files
- Generate an unsigned APK using APkBuilder
- Apksigner is used to sign the Apk to generate the final Apk
2.2 How Do I Manually Pack packets using the CLI
The usual development is built using Gradle. Below, we do not rely on Gradle. We directly use the official packaging tool for each stage to type an APK by hand, so that we can have a better and more detailed understanding of the process. Here we use the Simpleapk project directly as an example. The official packaging tool is in the directory android_sdk/build-tools/version/.
To begin, we will create a TMP directory to store the intermediates. Create TMP /final to hold the end product.
1. AAPT2 compile resources
Using AAPT2 to process resources requires two steps, compile and link, and the compile operation is performed first. Execute the following command.
/Users/zy/ android-sdK-mac_x86 /build-tools/28.0.2/ aapT2 compile -o TMP /res --dir SRC /main/res/Copy the code
Processing a single resource using AAPT2 produces xxx.flat files, which are intermediate products of AAPT2 and can be used for subsequent incremental compilation of resources. We specify the directory of the resource directly with –dir, and the product res is a compressed package containing xxx.flat after processing all the resources. Here let’s unzip the RES package again. Execute the following command.
unzip -u tmp/res -d tmp/aapt2_res
Copy the code
When we’re done, the directory looks like this.
TMP / ├ ─ ─ aapt2_res │ └ ─ ─ XXX. Flat ├ ─ ─ final └ ─ ─ resCopy the code
2. AAPT2 link resources
AAPT2 link is a complete resource. Arsc, binary resource and R. Java after compiling the xxx.flat resource link in the previous step. Execute the following command.
/Users/zy/ android-sdK-mac_x86 /build-tools/28.0.2/ AAPT2 link-o TMP/res.apk-i /Users/zy/android-sdk-mac_x86/platforms/android-28/android.jar --manifest src/main/AndroidManifest.xml --java tmp -R tmp/aapt2_res/drawable-hdpi_ic_launcher_foreground.xml.flat -R tmp/aapt2_res/mipmap-anydpi-v26_ic_launcher_round.xml.flat -R tmp/aapt2_res/mipmap-xhdpi_ic_launcher_round.png.flat -R tmp/aapt2_res/values_colors.arsc.flat -R tmp/aapt2_res/drawable-hdpi_ic_launcher_foreground1.xml.flat -R tmp/aapt2_res/mipmap-hdpi_ic_launcher.png.flat -R tmp/aapt2_res/mipmap-xxhdpi_ic_launcher.png.flat -R tmp/aapt2_res/values_strings.arsc.flat -R tmp/aapt2_res/drawable-mdpi_ic_launcher_foreground.xml.flat -R tmp/aapt2_res/mipmap-hdpi_ic_launcher_round.png.flat -R tmp/aapt2_res/mipmap-xxhdpi_ic_launcher_round.png.flat -R tmp/aapt2_res/values_styles.arsc.flat -R tmp/aapt2_res/drawable_ic_launcher_background.xml.flat -R tmp/aapt2_res/mipmap-mdpi_ic_launcher.png.flat -R tmp/aapt2_res/mipmap-xxxhdpi_ic_launcher.png.flat -R tmp/aapt2_res/layout_activity_main.xml.flat -R tmp/aapt2_res/mipmap-mdpi_ic_launcher_round.png.flat -R tmp/aapt2_res/mipmap-xxxhdpi_ic_launcher_round.png.flat -R tmp/aapt2_res/mipmap-anydpi-v26_ic_launcher.xml.flat -R tmp/aapt2_res/mipmap-xhdpi_ic_launcher.png.flat --auto-add-overlayCopy the code
After executing the command, res.apk is generated, which contains resource. Arsc, the processed Androidmanifest.xml, and the processed binary resources. We also decompress it here and use it in the final packaging. Run the following command.
unzip -u tmp/res.apk -d tmp/final
Copy the code
At the end of this step, the directory state is as follows.
TMP / ├ ─ ─ aapt2_res │ └ ─ ─ XXX. Flat ├ ─ ─ com │ └ ─ ─ zy │ └ ─ ─ simpleapk │ └ ─ ─ R.j ava ├ ─ ─ final │ ├ ─ ─ AndroidManifest. XML │ ├ ─ ─ res │ │ ├ ─ ─ drawable │ │ │ └ ─ ─ ic_launcher_background. XML │ │ ├ ─ ─ XXX resource directory │ └ ─ ─ resources. The arsc ├ ─ ─ res └ ─ ─ res. The apkCopy the code
3. Javac generates a class file
In this step we need to process the Java file and generate the class file. Use the R.java file generated in the previous step. Execute the following command.
javac -d tmp src/main/java/com/zy/simpleapk/MainActivity.java tmp/com/zy/simpleapk/R.java -cp /Users/zy/android-sdk-mac_x86/platforms/android-28/android.jar
Copy the code
At the end of this step, the directory state is as follows.
TMP / ├ ─ ─ aapt2_res │ └ ─ ─ XXX. Flat ├ ─ ─ com │ └ ─ ─ zy │ └ ─ ─ simpleapk │ ├ ─ ─ MainActivity. Class │ ├ ─ ─ R$color.class │ ├ ─ ─ R$drawable.class │ ├ ─ ─ R$layout.class │ ├ ─ ─ R$mipmap.class │ ├ ─ ─ R$string.class │ ├ ─ ─ R$style.class │ ├ ─ ─ R.c lass │ └ ─ ─ R.j ava ├ ─ ─ final │ ├ ─ ─ AndroidManifest. XML │ ├ ─ ─ res │ │ ├ ─ ─ drawable │ │ │ └ ─ ─ │ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├.txtCopy the code
4. D8 Compiles dex
This step is to compile the class file generated in the previous step into a dex file, using either D8 or dx, in this case D8. Execute the following command.
/Users/zy/ android-sdK-mac_x86 /build-tools/28.0.2/d8 TMP /com/zy/simpleapk/*. Class --output TMP --lib /Users/zy/android-sdk-mac_x86/platforms/android-28/android.jarCopy the code
After the command is executed, classes.dex is generated, which is the dex file that will be used in the final APK, and we will copy it to the final/ directory. Run the following command.
cp tmp/classes.dex tmp/final/classes.dex
Copy the code
At the end of this step, the directory state is as follows.
TMP / ├ ─ ─ aapt2_res │ └ ─ ─ XXX. Flat ├ ─ ─ classes. Dex ├ ─ ─ com │ └ ─ ─ zy │ └ ─ ─ simpleapk │ ├ ─ ─ MainActivity. Class │ ├ ─ ─ R$color.class │ ├ ─ ─ R$drawable.class │ ├ ─ ─ R$layout.class │ ├ ─ ─ R$mipmap.class │ ├ ─ ─ R$string.class │ ├ ─ ─ R$style.class │ ├ ─ ─ R.c lass │ └ ─ ─ R.j ava ├ ─ ─ final │ ├ ─ ─ AndroidManifest. XML │ ├ ─ ─ classes. Dex │ ├ ─ ─ res │ │ ├ ─ ─ drawable │ │ │ └ ─ ─ ic_launcher_background. XML │ │ ├ ─ ─ XXX resource directory │ └ ─ ─ resources. The arsc ├ ─ ─ res └ ─ ─ res. The apkCopy the code
5. Packaging apk
After executing the above command, all the materials needed to package APK are ready, because APK itself is zip format, here we directly package the above products with zip command, to generate final.apk. Execute the following command.
zip -r final.apk *
Copy the code
6. Apk signature
The packaged APK from the previous step cannot be installed directly because there is no signature. Here we use debug.keystore to sign final.apk. Execute the following command.
/Users/zy/ android-sdK-mac_x86 /build-tools/28.0.2/apksigner sign --ks ~/.android/debug.keystore final.apkCopy the code
You need to enter the debug.keystore password, which is Android.
In this way, the final. Apk is the apK generated manually. You can install it and try it out
2.3 AS packaging process
In normal development, we mostly just click Run in AS to compile an APK. Let’s start by looking at what happens when we click Run in AS. Click Run and open Build TAB to see the output. Gradle Task assembleDebug is executed.
Let’s add a line of output to build.gradle to see the extra parameters that AS adds to Gradle Task.
println "projectProperties: " + project.gradle.startParameter.projectProperties
Copy the code
The output is as follows:
projectProperties: [android.injected.build.density:xhdpi, android.injected.build.api:26, android.injected.invoked.from.ide:true, android.injected.build.abi:x86]
Copy the code
One of the parameters of the android. Injected. Invoked. From the ide = true indicates that the orders from the AS call Gradle. The next step is to execute Gradle’s package command. Gradle’s packaging commands execute tasks to generate the files needed for packaging. If you are not familiar with this aspect, you can take a look at the main process analysis of Android Gradle Plugin.
Three, multi-channel packaging
The packaging process of Android APK was introduced above, and I also experienced the whole process by typing APK manually. In the actual production and development process, we tend to send APK to various application markets. In many cases, we need to conduct some statistics according to the market channels, so we need to distinguish different market channels. There are many solutions to the problem of multi-channel packaging. The easiest thing to think of is to use Gradle’s Flavor and use Flavor’s features to generate different channel identifiers. However, this approach requires a build process for each channel package generated, which is very time consuming. Another way is to decompile APK using apkTool, modify resources, add channel identifiers, and repackage signatures. This method saves the build process, decompile, package, and signature steps, which are time-consuming. According to meituan’s blog, it takes nearly three hours to pack 900 channel packages.
So we need to find another way. Before we do that, let’s take a look at the way Android APK is signed. Understand the signature method, to better understand the method implementation.
Before we look at APK signatures, let’s take a look at the Zip file format, because APK is essentially a Zip file, and multichannel packaging involves Zip file formats.
3.1 Zip file format
APK is essentially a Zip file, which we can take a look at with the file command. It is a V2.0 Zip file.
The format of a Zip file is as follows:
It can be divided into three areas: Data area Central Directory End of Central Directory Record (EODR)
3.1.1 data area
This section records compressed file information and raw file data. Each file information is described by [local file header + file data + data Descriptor]. The local file header records compressed file information. The main contents are as follows:
description | length | content |
---|---|---|
local file header signature | 4 bytes (0x04034b50) | File header identifier, 0x04034B50 |
version needed to extract | 2 bytes | The lowest version required for decompression |
general purpose bit flag | 2 bytes | Universal flag bit |
compression method | 2 bytes | Compression way |
last mod file time | 2 bytes | The time when the file was last modified |
last mod file date | 2 bytes | Date the file was last modified |
crc-32 | 4 bytes | CRC – 32 check code |
compressed size | 4 bytes | Compressed file size |
uncompressed size | 4 bytes | Uncompressed file size |
file name length | 2 bytes | File name length |
extra field length | 2 bytes | Extension length |
file name (variable size) | The file name | |
extra field (variable size) | Extension data |
File Data Stores compressed file data
data descriptor
description | length | content |
---|---|---|
crc-32 | 4 bytes | Crc32 checksum |
compressed size | 4 bytes | Size of compressed file |
uncompressed size | 4 bytes | Size of an uncompressed file |
Indicates the end of the file. This parameter is displayed only when the third bit of the General Purpose bit Flag in the Local file header is 3. Not normally
3.1.2 Core Directory
The core directory holds more information about compressed files and support for Zip64. There are two main parts: File Header and Digital Signature
File header is a more specific description of a file. Each file header corresponds to a local file header.
description | length | content |
---|---|---|
central file header signature | 4 bytes (0x02014b50) | Core file header flag, magic number 0x02014b50 |
version made by | 2 bytes | The version used for compression |
version needed to extract | 2 bytes | Decompress the required version |
general purpose bit flag | 2 bytes | Universal bit mark |
compression method | 2 bytes | Compression method |
last mod file time | 2 bytes | The time when the file was last modified |
last mod file date | 2 bytes | Date the file was last modified |
crc-32 | 4 bytes | CRC – 32 check code |
compressed size | 4 bytes | Compressed file size |
uncompressed size | 4 bytes | File size before compression |
file name length | 2 bytes | File name length |
extra field length | 2 bytes | Extension length |
file comment length | 2 bytes | File comment length |
disk number start | 2 bytes | The starting location of the file on the disk |
internal file attributes | 2 bytes | Internal file attributes |
external file attributes | 4 bytes | External file properties |
relative offset of local header | 4 bytes | Offset of the corresponding Local file header |
file name (variable size) | The file name | |
extra field (variable size) | Extension data | |
file comment (variable size) | File comment |
The official documentation of Digital Signature does not provide details about Digital signature, but it does mention that the core directory can be compressed and encrypted, and blind guess is used to encrypt the core directory.
description | length | content |
---|---|---|
header signature | 4 bytes (0x05054b50) | |
size of data | 2 bytes | |
signature data (variable size) |
3.1.3 End of the Core Directory
The end of directory flag is used to mark the end of compressed directory data at the end of the entire file. Each Zip file has one and only one closing flag record.
description | length | content |
---|---|---|
end of central dir signature | 4 bytes (0x06054b50) | End of directory flag, magic 0x06054B50 |
number of this disk | 2 bytes | Current Disk Number |
number of the disk with the start of the central directory | 2 bytes | Number of the disk at the start of the core directory |
total number of entries in the central directory on this disk | 2 bytes | The number of core directories recorded on disk |
total number of entries in the central directory | 2 bytes | Number of core directory structures |
size of the central directory | 4 bytes | Core directory size |
offset of start of central directory with respect to the starting disk number | 4 bytes | The offset of the starting location of the core directory |
.ZIP file comment length | 2 bytes | Note the length of the |
.ZIP file comment (variable size) | The comment |
This is basically a generic Zip package structure. Here’s a look at Android’s signature mechanism.
3.2 V1 signature
The mechanism of V1 signature is mainly in the three files in the meta-INF directory, manifest. MF, cert. SF, cert. RSA, which are the products of V1 signature. Manifest.mf holds a summary of all files, which reads as follows:
Manifest-version: 1.0 built-in: Generated- By -ADT Created-By: Android Gradle 3.4.0 Name: AndroidManifest.xml SHA-256-Digest: QxJh66y6ssDSNFgZSlf5jIWXfRdWnqL1c3BSwSDUYLQ= Name: META-INF/android.arch.core_runtime.version SHA-256-Digest: zFL2eISLgUNzdXtGA4O/YZYOSUPCA3Na3eCjULPlCYk=Copy the code
Cert.sf saves a summary of each message in manifest.mf, which reads in part as follows:
Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA-256-Digest-Manifest: j8YGFgHsujCHud09pT6Igh21XQKSnG+Gqy8VUE55u+g=
X-Android-APK-Signed: 2
Name: AndroidManifest.xml
SHA-256-Digest: qLofC3g32qJ5LmbjO/qeccx2Ie/PPpWSEPBIUPrlKlY=
Name: META-INF/android.arch.core_runtime.version
SHA-256-Digest: I65bgli5vdqHKel7MD74YlSuuyCR/5NDrXr2kf5FigA=
Copy the code
Cert. RSA contains the signature of the cert. SF file and the certificate used for the signature.
The main process of APK signature is as follows:
To calculate the file summary in APK, save the base64 encoding of the summary into the manifest.mf file -> Calculate the summary of the manifest.mf file, Save its base64 encoding to cert. SF file -> calculate the summary of each data block in the manifest.mf file, save its base64 encoding to cert. SF file -> calculate the summary of cert. SF file, Calculate digital signature from developer private key -> Save digital signature and developer public key to cert. RSA fileCopy the code
During APK verification, the main process is as follows:
Decrypt digital certificate in cert. RSA and sign cert. SF by CA certificate -> verify whether cert. SF file has been modified by signature -> Verify whether manifest. MF file has been modified by cert. SF -> Verify by cert. SF Whether the data item in the manifest. MF file has been modified -> Check whether the file in the APK has been modified with the manifest. MF fileCopy the code
We talked about Zip file structure above, through the above verification process, we can find that in the APK verification process, only the content of the data area is verified, the remaining two parts are not processed. Therefore, if the remaining two parts are modified, the signature verification process will not be found, and the EOCD comment field is a good choice for writing information. Therefore, channel information can be written into the annotation field of EOCD to achieve the purpose of entering channel information. That’s what Tencent VasDolly does. In addition, we can find that the APK signature verification process does not carry the signature and verification of the files in the meta-INF folder. Therefore, we can add an empty file in the meta-INF folder to carry channel information. That’s what Meituan does.
3.3 V2 signature
Everything was fine with the above Scheme, until Android 7.0 introduced a new Signature Scheme, APK Signature Scheme V2, which made the multichannel Signature Scheme invalid on V1. Let’s first look at how V2 signatures work. The APK Signing Block is added between the data area and the core directory area to record the Signing information. The structure before and after the signature is as follows.
The APK Signing Block format is as follows:
description | length | content |
---|---|---|
size of block | 8 bytes | The length of the signature block |
ID-value | A series of id-values are saved | |
size of block | 8 bytes | The length of the signature block is the same as the first field |
magic | 16 bytes | Signature block marker, magic number |
The APK Signing Block is used to sign the other three modules. The Signing information is stored in the value corresponding to the ID 0x7109871A in the id-value. The following logic is used to verify signatures.
Check whether the signature block V2 is contained. If the signature block V2 is contained, use the V2 signature block for authentication. If the signature block V2 is not contained, use the V1 signature block for authentication. Therefore, the method of adding EOCD annotations and meta-INF empty files in V1 scheme is invalid when V2 signature is used for validation. Looking at this, we can see that the V2 signature validates the other three module data, but not the APK Signing Block itself, where the id-value is a set of data, so we can add an ID-value that contains channel information here. This is how multi-channel packages are generated under V2 signatures. For detailed code of the above principle, you can see github.com/Tencent/Vas… And github.com/Meituan-Dia…
3.4 the V3 signature
A new signature method V3 was introduced in Android 9.0. To solve the problem of signature expiration. V3 Signing adds a signature Block to V2’s APK Signing Block, which stores the supported SDK version and key flow structure. Since no major changes have been made to the V2 structure, there will be no impact on the multi-channel packaging scheme. For more detailed information about V3 signatures, see the official documentation.
Other packing methods
4.1 Multiple APKs
The APK structure and packaging mentioned above includes all resources, including adaptation to different resolutions, adaptation to different apis, etc., but in fact, on each user device, only one of these resources will be used, and the rest of the resources will only occupy the package size. So Google also provides other ways to reduce package sizes by different resolutions, different ABI’s, and so on. After Android L, Android supports Multiple APKs, which simply means that the same application can be packaged into Multiple APKs corresponding to different devices with different resolutions, CPU architectures and API levels. Then install different APKS on different devices to reduce the volume. The documentation provides a detailed explanation, so let’s take a look at what APK looks like by configuring different resolutions. Let’s start by adding the following configuration in build.gradle.
android {
...
splits {
// Configures multiple APKs based on screen density.
density {
// Configures multiple APKs based on screen density.
enable true
// Specifies a list of screen densities Gradle should not create multiple APKs for.
exclude "ldpi"."xxhdpi"."xxxhdpi"
// Specifies a list of compatible screen size settings for the manifest.
compatibleScreens 'small'.'normal'.'large'.'xlarge'}}}Copy the code
The meaning is clear enough, so I won’t explain it any more. We then compile and see that instead of one APK, four apKs are generated.
Simpleapk-universal-debug. apk is a complete APK containing all resources. The other three APKs were generated at different resolutions. It contains only the resources required for the corresponding resolution. This reduces the package size. We can see the difference by comparing the two APKs.
4.2 the Split APKs
Android L later provides Split APKs multi-APK construction mechanism, which can Split an APK into multiple APKs based on CPU architecture and screen density. When downloading an application, the corresponding APK will be installed according to the current device configuration. When Split APKs are packaged, one Base APK and multiple Split APKs are generated. Base APK contains generic resources, while Split APK is user device-specific. The user installs Base APK and its device’s corresponding Split APK, reducing the package size of other useless resources. We don’t generate Split APKs in normal development, but we can open Instant Run to compile the app. Instant Run now runs on Android 5.0 devices using Split APKs. Use Instant after the Run, in the build/intermediates/split – apk/debug/slices/below, you can see the generated split APKs, we choose one of them, in the manifest file, Defines the split tag, which represents the current Split APK number.
In the build/intermediates/instant – run – apk/debug/below, is the Base apk that contains some common resources.
Adb shell PM install-create adb shell PM install-write adb shell PM install-write Adb shell PM install-commit For details, see here.
4.3 App Bundle
App Bundle is a new dynamic framework for apps introduced at Google I/O 2018. It is a dynamic loading method based on the principle of Split APKs. The comparison with the traditional installation mode is as follows.
The App Bundle depends on the.aab file. The structure of an AAB is as follows.
Is general base/resources, is based on certain feature1 feature2 / screen density, distinguish the specific resources, including CPU architecture.
Let’s take a look at App Bundle usage using Simpleapk Demo. To use the App Bundle, you need to create a Base Module and feature Module. Our Simpleapk is the Base Module and then creates a feature module. Note that the feature Module needs to select Dynamic Feature Module.
dependencies {
implementation fileTree(dir: 'libs'.include: ['*.jar'])
implementation project(':simpleapk')}Copy the code
Then dynamicFeatures is configured in the base module.
android {
bundle {
// ...
}
dynamicFeatures = [":dynamic_feature"]}Copy the code
We bundleDebug command in the project, you can see, in the build/outputs/bunudle/debug directory are created XXX. Aab file, Google will Play according to XXX. Aab generate required for users to install the APK. Aab is also a zip file. The contents of the bag are as follows.
We can generate APK from.aab files using bundletool provided by Google. Execute the following command.
Java jar bundletool - all - 0.10.2. Jar build - apks - bundle = build/outputs/bundle/debug/simpleapk aab -- the output = bundle. ApksCopy the code
The generated bundle.apks file is also a zip file, so let’s unzip it.
The bundle.apks contains two directories, the Base APK and Split APK and the other one standalones/, which have the complete APK generated according to the different screen densities. Used to install App Bundles on devices that do not support them.
Of course, the App Bundle is completely dependent on Google Play, so it is still not available in China. But by understanding how this works, we can also see how Google reduces package sizes.
conclusion
The resources
Zip file format
Pkware.cachefly.net/webdocs/APP… Blog.csdn.net/a200710716/…
The signature
Source.android.com/security/ap… Source.android.com/security/ap… zhuanlan.zhihu.com/p/26674427
Android Build Tools
Tech.meituan.com/2014/06/13/… Tech.meituan.com/2017/01/13/… Developer.android.com/studio/comm…
Multiple APKs
Developer.android.com/studio/buil…
Split APKs
www.infoq.cn/article/2Mg… Blog.csdn.net/ximsfei/art…
App Bundle
Developer.android.com/guide/app-b… Developer.android.com/studio/comm…