fwrite

fwrite

好好生活
twitter
github
email

App Size Optimization

App Size Optimization#

Uploading Apps Using Android App Bundle#

The simplest way to immediately reduce app size when publishing to Google Play is to publish the app as an Android App Bundle, a new upload format that contains all the compiled code and resources of the app, while APK generation and signing are deferred to the Google Play Store.
Google Play's new app delivery model is called "Dynamic Delivery," which uses your App Bundle to generate and deliver optimized APKs tailored to each user's device configuration, so users only need to download the code and resources necessary to run your app. You no longer need to compile, sign, and manage multiple APKs to support different devices, and users can receive smaller, optimized download packages.
Please note that Google Play enforces a compressed download size limit of no more than 150 MB for apps published using App Bundles, so we still recommend following the guidelines presented on this page to minimize the download size of your app as much as possible.
For apps published to Google Play by uploading signed APKs, the compressed download size limit is no more than 100 MB.

Understanding APK Structure#

Before discussing how to reduce app size, it is helpful to understand the structure of an APK. An APK file consists of a Zip archive containing all the files that make up the app. These files include Java class files, resource files, and files containing compiled resources.

An APK contains the following directories:

  • META-INF/: Contains the CERT.SF and CERT.RSA signature files, as well as the MANIFEST.MF manifest file.

  • assets/: Contains the app's resources; the app can retrieve these resources using the AssetManager object.

  • res/: Contains resources that are not compiled into resources.arsc.

  • lib/: Contains compiled code specific to the processor software layer. This directory contains subdirectories for each platform type, such as armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, and mips.

The APK also contains the following files. Among these files, only AndroidManifest.xml is required.

  • resources.arsc: Contains compiled resources. This file includes the XML content from all configurations in the res/values/ folder. The packaging tool extracts this XML content, compiles it into binary form, and archives the corresponding content. This content includes language strings and styles, as well as paths to content not directly included in the resources.arsc file (such as layout files and images).

  • classes.dex: Contains classes compiled in the DEX file format understandable by the Dalvik/ART virtual machine.

  • AndroidManifest.xml: Contains the core Android manifest file. This file lists the app's name, version, permissions, and referenced library files. The file uses Android's binary XML format.

Reducing Resource Count and Size#

The size of the APK affects the app's loading speed, memory usage, and power consumption. One simple way to reduce APK size is to decrease the number and size of the resources it contains. Specifically, you can remove resources that the app no longer uses and replace image files with scalable Drawable objects. This section will discuss these methods and several other ways to reduce resources in the app to decrease the total APK size.

Removing Unused Resources#

The lint tool is a static code analyzer included with Android Studio that can detect resources in the res/ folder that are not referenced by code. When the lint tool finds potentially unused resources in the project, it displays a message, as shown in the following example.

    res/layout/preferences.xml: Warning: The resource R.layout.preferences appears to be unused [UnusedResources]

Note: The lint tool does not scan the assets/ folder, resources referenced via reflection, or library files linked to the app. Additionally, it does not remove resources; it only alerts you to their presence.

The libraries you add to your code may contain unused resources. If you enable shrinkResources in your app's build.gradle file, Gradle can automatically remove resources on your behalf.

android { // Other settings buildTypes 
  	{ 
          release 
       	{ 
              minifyEnabled true 
        		shrinkResources true 
        		proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
       	} 
      } 
}

To use shrinkResources, you must enable code shrinking. During the build process, ProGuard first removes unused code but retains unused resources. Then, Gradle removes unused resources.

For more details on ProGuard and other ways Android Studio helps you reduce APK size, see Shrinking Code and Resources.

In Android Gradle Plugin version 0.7 and higher, you can declare the configurations supported by your app. Gradle will pass this information to the build system using the resConfig and resConfigs styles and the defaultConfig option. The build system will then prevent resources from unsupported configurations from appearing in the APK, thereby reducing the APK size. For details on this feature, see Removing Unused Alternate Resources.

Minimizing Resource Usage in Libraries#

When developing Android apps, you often need to use external libraries to enhance the app's usability and functionality. For example, you might reference the Android Support Library to improve user experience on older devices or use Google Play Services to retrieve automatic translations of text in the app.

If a library is designed for servers or desktop devices, it may contain many objects and methods that the app does not need. To include only the parts of the library that your app requires, you can edit the library files (if the corresponding license allows you to modify the library). You can also use other libraries that are more suitable for mobile devices to add specific features to your app.

Note: ProGuard can clean up some unnecessary code imported with libraries, but it cannot remove large internal dependencies of the library.

Supporting Only Specific Densities#

Android supports a wide range of devices (with various screen densities). In Android 4.4 (API level 19) and higher, the framework supports various densities: ldpi, mdpi, tvdpi, hdpi, xhdpi, xxhdpi, and xxxhdpi. Although Android supports all these densities, you do not need to export rasterized resources to every density.
If you know that only a small portion of users have devices with a specific density, consider whether you need to bundle those densities into your app. If you do not add resources for specific screen densities, Android will automatically scale existing resources originally designed for other screen densities.
If your app only requires scaled images, you can save more space by using a single variant of the image in drawable-nodpi/. We recommend that each app include at least one xxhdpi image variant.
For details on screen densities, see Screen Sizes and Densities.

Using Drawable Objects#

Some images do not require static image resources; the framework can dynamically draw images at runtime instead. Drawable objects (represented as <shape> in XML) take up little space in the APK. Additionally, XML Drawable objects generate monochrome images that comply with Material Design guidelines.

Reusing Resources#

You can add separate resources for variants of an image, such as a version of the same image with a different hue, shadow settings, or rotation. However, we recommend reusing the same set of resources and customizing them at runtime as needed.
Android provides several utilities to change the color of resources, each of which uses the android:tint and tintMode attributes on Android 5.0 (API level 21) and higher. For lower platform versions, the ColorFilter class is used.
You can also omit resources that are merely the rotated equivalent of another resource. The following code snippet provides an example of transforming "thumbs up" into "thumbs down" by rotating 180 degrees around the center of the image:

<?xml version="1.0" encoding="utf-8"?> 
<rotate xmlns:android="http://schemas.android.com/apk/res/android" 
	android:drawable="@drawable/ic_thumb_up" 
	android:pivotX="50%" 
	android:pivotY="50%" 
	android:fromDegrees="180" />

Rendering from Code#

You can also reduce APK size by rendering images programmatically. Programmatic rendering can free up space since you no longer store image files in the APK.

Compressing PNG Files#

The aapt tool can optimize image resources placed in res/drawable/ through lossless compression during the build process. For example, the aapt tool can convert true-color PNGs that do not require more than 256 colors into 8-bit PNGs using a palette. This results in images of the same quality but with a smaller memory footprint.

Keep in mind that aapt has the following limitations:

  • The aapt tool does not compress PNG files contained in the assets/ folder.
  • Image files need to use 256 colors or fewer for the aapt tool to optimize them.
  • The aapt tool may increase the size of already compressed PNG files. To prevent this, you can disable this process for PNG files using the cruncherEnabled flag in Gradle: aaptOptions { cruncherEnabled = false }

Compressing PNG and JPEG Files#

You can use tools like pngcrush, pngquant, or zopflipng to reduce the size of PNG files without losing image quality. All these tools can decrease the size of PNG files while maintaining visually perceived quality.

The pngcrush tool is particularly effective: it iterates through PNG filters and zlib (Deflate) parameters, using each combination of filters and parameters to compress the image. It then selects the configuration that produces the smallest compressed output.

To compress JPEG files, you can use tools like packJPG and guetzli.

Using WebP File Format#

If targeting Android 3.2 (API level 13) and higher, you can also use images in the WebP file format (instead of using PNG or JPEG files). The WebP format provides lossy compression (like JPEG) and transparency (like PNG), but it can offer better compression than JPEG or PNG.

You can use Android Studio to convert existing BMP, JPG, PNG, or static GIF images to WebP format. For details, see Creating WebP Images with Android Studio.

Note: Google Play only accepts APKs if the launcher icon uses the PNG format.

Using Vector Graphics#

You can create resolution-independent icons and other scalable media using vector graphics. Using these graphics can significantly reduce the space occupied by the APK. Vector images are represented in Android as VectorDrawable objects. With VectorDrawable objects, a 100-byte file can generate a clear image that matches the screen size.

However, the system takes a considerable amount of time to render each VectorDrawable object, and larger images take longer to display on the screen. Therefore, consider using these vector graphics only for displaying small images.

For details on using VectorDrawable objects, see Using Drawable Resources.

Using Vector Graphics for Animated Images

Do not use AnimationDrawable to create frame-by-frame animations, as this requires adding separate bitmap files for each frame of the animation, which significantly increases the size of the APK.

Instead, you should use AnimatedVectorDrawableCompat to create animated vector drawable resources.

Reducing Native and Java Code#

You can use various methods to reduce the size of Java and native code libraries in your app.

  1. Remove Unnecessary Generated Code

Ensure you understand the space occupied by any automatically generated code. For example, many protocol buffer tools generate excessive methods and classes, which can double or triple the size of the app.

  1. Avoid Using Enums

A single enum can increase the size of the app's classes.dex file by about 1.0 to 1.4 KB. These size increases can quickly accumulate, resulting in complex systems or shared libraries. If possible, consider using @IntDef annotations and ProGuard to remove enums and convert them to integers. This type conversion retains the various safety advantages of enums.

  1. Reduce the Size of Native Binaries

If your app uses native code and the Android NDK, you can also reduce the size of the release version of the app by optimizing the code. Removing debug symbols and not extracting native libraries are two practical techniques.

  1. Remove Debug Symbols

If the app is in development and still requires debugging, using debug symbols is appropriate. You can use the arm-eabi-strip tool provided in the Android NDK to remove unnecessary debug symbols from native libraries. After that, you can compile the release version.

  1. Avoid Extracting Native Libraries

When compiling the release version of the app, you can package uncompressed .so files in the APK by setting android:extractNativeLibs="false" in the element of the app manifest. Disabling this flag prevents the PackageManager from copying .so files from the APK to the file system during installation, providing the additional benefit of reducing app updates.

Maintaining Multiple Slim APKs#

APKs may contain content that users download but never use, such as resources for other languages or screen densities. To ensure users receive the smallest download file, you should upload your app using Android App Bundle to Google Play. By uploading an App Bundle, Google Play can generate and deliver optimized APKs tailored to each user's device configuration, so users only need to download the code and resources necessary to run your app. You no longer need to compile, sign, and manage multiple APKs to support different devices, and users can receive smaller, optimized download packages.

If you do not plan to publish your app on Google Play, you can split the app into multiple APKs and differentiate them based on factors such as screen size or GPU texture support.

When users download your app, their devices will receive the correct APK based on the device's capabilities and settings. This way, the device does not receive resources for features that the device does not possess. For example, if a user has an hdpi device, there is no need for the xxxhdpi resources you may provide for devices with higher density displays.

For details, see Configuring APK Splits and Maintaining Multiple APKs.

Reducing Native Size#

android {
    ...
        defaultConfig {
            ...
                // Configure the externalNativeBuild block under the defaultConfig node
                externalNativeBuild {
                    // For ndk-build, instead use ndkBuild {}
                    cmake { // Specify cmake compilation options
                        // Sets optional flags for the C compiler.
                        cFlags "-fvisibility=hidden  -ffunction-sections -fdata-sections"
                        // Sets optional flags for the C++ compiler.
                        cppFlags "-fvisibility=hidden  -ffunction-sections -fdata-sections -std=c++11"
                        arguments "-DANDROID_TOOLCHAIN=gcc" // Set to use gcc for compilation; cmake defaults to using clang
                    }
                }
        }
    buildTypes {...}
}
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.