The importance of the size of the application installation package is needless to say. The size of the application installation package directly affects the download and retention of users, and some carriers even require the size to be smaller than a certain value. However, with the iterative development of the business, the application will become larger and the size of the installation package will continue to grow. As the size of the installation package increases, the installation time, running memory and ROM space of the application will also increase, so the slimming of APK has to be considered.

The constitution of the Apk

You can use the Analyze APK tool delivered with Android Studio to Analyze the APK.

To automatically open apK using Analyze APk, drag the APK directly from your computer into Android Studio. Then, we can see the absolute size of APK files and the percentage of each component file, as shown below:

It can be seen that APK mainly consists of the following parts:

Files/Directories describe
lib/ Armeabi, armeabi-v7A, arm64-V8A, x86, x86_64, MIPS. In most cases, you only need to support armABI and x86 architecture. If not necessary, you can consider removing x86 parts
res/ Store compiled resource files, such as drawable, layout, etc
assets/ Application resources that the application can useAssetManagerTo retrieve the resource
META-INF/ This folder is generally stored in the signed APK, which contains information such as the signature summary of all files in the APK
classes(n).dex Classes files are Java classes that are compiled by DEX and understood by the Dalvik/ART virtual machine
resources.arsc Compiled binary resource files
AndroidManifest.xml Android manifest file, format is AXML, used to describe the application name, version, required permissions, registered four components

Now that I know the components of APK and what they do, I’m ready to slim down APK in the following ways:

DEX optimization

Dex is an executable file of the Android system, which contains all operation instructions and runtime data of an application. Dex generally takes up a large proportion of the application package volume, and the more Dex there are, the longer the installation time of the App will be. Therefore, optimizing them is a top priority. Let’s take a look at ways to optimize the volume of the Dex section.

Configure ProGuard

Code compression can be achieved by enabling ProGuard and adding minifyEnabled True to the corresponding build type in the build.gradle file.

Code compression can slow down builds, so it should be avoided in debug builds whenever possible. However, be sure to enable code compression for the final APK used for testing, as failure to adequately customize the code to be retained can introduce errors.

The configuration is as follows:

buildTypes {
    release {
        // 1
        minifyEnabled true
        // 2. Enable zipAlign to align resources in the installation package with 4 bytes, which reduces the memory consumption of the application at runtime
        zipAlignEnabled true
        // Remove useless resource files: When ProGuard removes some useless resource files,
        // Resources referenced by this code are also marked as useless and removed by resource compression.
        // Note that the resource compressor currently does not remove resources (such as strings, sizes, styles, and colors) defined in the values/ folder
        shrinkResources true
        Proguard-android. TXT is the SDK default obturation configuration.
        / / it is located the location of the android SDK/tools/proguard/proguard - android. TXT.
        // In addition, proguard-Android-optimize.txt is also the SDK's default obtuse configuration,
        // But it turns optimization on by default. Also, we can set android.util.log to invalid code in configuration obfuscations,
        // To remove the code that prints the log in apK. Proguard-rules. pro is an obfuscated configuration under this module.
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.release
    }
}

Copy the code

${project.buildDir}/outputs/mapping/${flavorDir}/ ${flavorDir}/ ${project.buildDir}/outputs/ ${flavorDir}/

The file name describe
dump.txt The internal structure of all class files in APK
mapping.txt Provides conversions between raw and obfuscated class, method, and field names that can be converted throughproguard.obfuscate.MappingReaderTo resolve
seeds.txt Lists classes and members that are not confused
usage.txt Lists the code removed from APK

We need to take a close look at the final merged ProGuard profile to see if there is excessive keep.

You can add the following rules to the obfuscation configuration to output the final configuration of ProGuard, especially the various keep * types. In many cases, you just need to keep a package, a method, or a class name.

Print the final configuration of ProGuard
-printconfiguration configuration.txt
Copy the code

Some of the output configuration results:

.
-keep class com.lutongnet.kalaok2.helper.RequestStatus {
    <fields>;
    <methods>;
}

-keep class com.lutongnet.kalaok2.util.RxView {
    <fields>;
    <methods>;
}

-keep class okhttp3.internal.** {
    <fields>;
    <methods>;
}

-keep class retrofit2.** {
    <fields>;
    <methods>;
}
.
Copy the code

Remove useless code

As the project continues to iterate and update, the amount of code increases, resulting in more useless code. When deleting useless code, we often don’t want to remove it because there is too much code. Therefore, we can use the built-in Lint tool of Android Studio to detect invalid code, as follows:

  • Click the Analyze menu bar
  • Select Run Inspection by Name
  • Enter unused declaration in the text box that is displayed
  • Choose Moudule ‘app’
  • Click ok

It will automatically scan for useless code and select delete. At the same time, you can check for duplicate code by using Simian tools offline. In addition, there are some other tips, such as reducing the use of enums (see Remove Enumerations). Each reduction in ENUM reduces the size of the enums by approximately 1.0 to 1.4 KB. Remove all dependencies that are useless or duplicate.

Resource slimming optimization

As mentioned above, DEX and resource files occupy the most space in an APK. Therefore, optimization of resource files is particularly important. Optimization of resources can be carried out from the following aspects:

Optimization of redundant resources

Applications iterate over a long period of time, and there are always unused resources that occupy the volume of the installation package, even though they will not be used during the execution of the application.

1. Use Lint to delete unwanted resources

Unused Resources can be scanned using the static code scanning tool that comes with Android Studio. Specific use is as follows:

  • Click the Analyze menu bar
  • Select Run Inspection by Name
  • Enter unused Resources in the text box that is displayed
  • Choose Moudule ‘app’ or Whole Project
  • Click ok

The scan results are as follows:

To Remove unwanted Resources, click **”Remove All Unsed Resources” **, and with Lint, you can also Remove unused Resources in XML resource files, such as unused colors in colors.xml, etc. Lint as a static scanning tool, its biggest problem is that it doesn’t take into account ProGuard code clipping. During ProGuard we shrink a lot of useless code, but the Lint tool cannot detect the useless resources referenced by the useless code.

2. Optimize shrinkResources

To enable shrinkResources, set shrinkResources to true in the build.gradle file. Such as:

android {
    ...
    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                    'proguard-rules.pro'}}}Copy the code

Note that resource compressors currently do not remove resources (such as strings, sizes, styles, and colors) defined in values/ folders, so they can be used in conjunction with Lint.

Image compression

In general, 1000 lines of code in the APK can take up to 5 KB, and pictures, generally has several dozens to hundreds of KB, so, for image compression, it gains obvious is greater, but often the UI designer or developer if forgot to add images compression of graph is added, the package volume will increase a lot. For picture compression, we can compress pictures on the website Tinypng, but if there are too many pictures in the App, it is very troublesome to compress them one by one. Therefore, we can use McImage, TinyPngPlugin or TinyPIC_Gradle_Plugin to automate batch compression of images. However, it is important to note that in the Android build process, AAPT uses built-in compression algorithms to optimize PNG images in the res/drawable/ directory. However, this may cause the size of the already optimized images to become larger. Therefore, You can disable AAPT to optimize PNG images by setting cruncherEnabled in build.gradle, as shown below:

aaptOptions {
    cruncherEnabled = false
}
Copy the code

In addition, we should also pay attention to the choice of image format, for we generally use more PNG or JPG format, the same image converted to WebP format will have a significant compression. For PNG, it is a lossless format, while JPG is lossy. JPG is often used to process color images depending on the compression rate, and sometimes it will remove the colors that are relatively small in our visual recognition, while PNG will strictly retain all colors. As a result, PNG is significantly larger than JPG when the image is large or brightly colored.

For the conversion and compression of Webp pictures, please refer to this article.

So library optimization

So is a dynamic link library on Android. In our Android application development process, sometimes Java code cannot meet the requirements, such as some encryption and decryption algorithms or audio and video codec functions, So it must be realized through C or C++. Then generate So file for the Java layer to call, in the generation of So file need to consider the generation of different mobile PHONE CPU architecture files on the market. Currently, Android supports seven different types of CPU architectures, such as the common Armeabi, Armeabi-V7A, X86, and more. Theoretically, the CPU of the corresponding architecture has the highest execution efficiency, but this will result in more So files of each platform architecture stored in the lib directory, So the volume of App will naturally be larger.

Therefore, we need to reduce the lib directory, we configure the abiFiliters in build.gradle to set the So schema supported by the App with the following configuration code:

defaultConfig {
     //so library compatibility, most use Armeabi, if necessary to add others
     ndk {
            abiFilters "armeabi"}}Copy the code

Resources to confuse

Similar to code obfuscation, resource obfuscation confuses resource paths into paths for individual resources. AndroidResGuard can be used here, which makes redundant resource paths shorter, such as changing res/drawable/bg to R /d/ A.

AndroidResGuard can be used as follows:

  1. Add the following plugin dependencies to the project root build.gradle file:

    classpath 'com. Tencent. Mm: AndResGuard - gradle - plugin: 1.2.17'
    Copy the code
  2. Add the plugin to the build.gradle file in the project Module:

    apply plugin: 'AndResGuard'
    Copy the code
  3. Add the configuration items for AndroidResGuard. The default Settings are as follows:

    andResGuard {
        // mappingFile = file("./resource_mapping.txt")
        mappingFile = null
        use7zip = true
        useSign = true
        // This switch will keep the original path of all resources, only the name of the resource
        keepRoot = false
        // Setting this value will confuse the arSC name column with the same name, reducing the size of the string constant pool
        fixedResName = "arg"
        // This will merge all resources with the same hash value, but do not rely too much on this function to remove redundant resources
        mergeDuplicatedRes = true
        whiteList = [
            // for your icon
            "R.drawable.icon".// for fabric
            "R.string.com.crashlytics.*".// for google-services
            "R.string.google_app_id"."R.string.gcm_defaultSenderId"."R.string.default_web_client_id"."R.string.ga_trackingId"."R.string.firebase_database_url"."R.string.google_api_key"."R.string.google_crash_reporting_api_key"
        ]
        compressFilePattern = [
            "*.png"."*.jpg"."*.jpeg"."*.gif",
        ]
        sevenzip {
            artifact = 'com. Tencent. Mm: SevenZip: 1.2.17'
            //path = "/usr/local/bin/7za"
        }
    
        /** * Optional: If not set, apk output by Assemble will be overridden by default
        // finalApkBackupPath = "${project.rootDir}/final.apk"
    
        /** * Optional: Specify the digest algorithm for generating JAR files when v1 is signed * the default value is sha-1 **/
        // digestalg = "SHA-256"
    }
    Copy the code
  4. Click on the module to the right of the project/Tasks/andresguard resguardRelease resources can generate confusion over the APK. As shown below:

The generated APK is as follows:

Drag the optimized APK into Android Studio for analysis, and the results are as follows:

As can be seen from the figure above, after optimization, APK is reduced from the original 22.1MB to 14.2MB, reduced by 7.9MB, and the amplitude reaches 35.75%. Moreover, after optimization, not only the code is obfuscated encryption, but also the resource files are obfuscated compression, which increases the decomcompiling difficulty of APK. The security of the application is enhanced. Therefore, apK optimization is very necessary.

conclusion

The above is a summary of some attempts and accumulation in APK slimming, which can be used according to the project and their own situation. Of course, there are other problems such as plug-ins to reduce the size of the installation package. Finally, cutting out unnecessary features is a great way to slim down your package. There are many aspects to the standard of a good App, but providing the smallest possible installation package is one of the important aspects, which is also the basic requirements for us Android developers themselves, to always maintain good programming habits and a keen sense of package size.