preface

As the complexity of your business increases, so do the code, resources, and the size of your APP. From the user level, faced with tens of megabytes of APP, you will still hesitate to download it under non-wifi conditions. If you do not download it, you may lose a user. At a company level, traffic is money, and reducing the size of your APP is important. On a developer level, it’s a little bit more rewarding to master the craft.

Without further ado, let’s get down to business.

A. The APK structure

Know yourself and know your enemy, and you can win a hundred battles. It is helpful to understand the structure of the application APK. The APK file consists of a ZIP archive that contains all the files that make up the application. These files include Java class files, resource files, and files containing compiled resources.

APK contains the following directories:

  • Meta-inf / : contains cert. SF and cert. RSA signature files as well as the manifest.mf MANIFEST file.
  • Assets / : Contains the application resources that the application can retrieve using the AssetManager object.
  • Res / : contains the uncompiled resource resources.arsc.
  • Lib / : contains compiled code specific to the processor software layer. This directory contains subdirectories for each platform, such as Armeabi, Armeabi-v7A, ARM64-V8A, x86, X86_64, and MIPS.
  • Resources.arsc: contains compiled resources. This file contains the XML content in all configurations of the RES /values/ folder. The packaging tool extracts this XML content, compiles it into binary format, and archives the content. This content includes language strings and styles, as well as content paths, such as layout files and images, that are directly contained in the resources.arsc file.
  • Classes.dex: contains classes compiled in a dex file format that the Dalvik/ART virtual machine understands.
  • Androidmanifest.xml: Contains the core Android manifest file. This file lists the application name, version, access rights, and referenced library files. This file uses Android’s binary XML format.
  • Take a look at the file directory after unzip of Taobao APP

In general, the larger parts of an APK structure are classes.dex, lib, RES, assets and other files or directories. Therefore, the following four cases will be explained.

In addition, we can analyze APK by APK Analyser;

Reduce classes.dex

Classes.dex contains all the Java code. When you compile your application, Gradle will convert the.class files in all your modules into.dex files and combine them into a classes.dex file.

A single classes.dex file can hold about 64K methods. If you reach this limit, you must enable multidexing in your project. This will create another classes1.dex file to store the rest of the methods. So the number of classes.dex files depends on the number of methods you have.

  • Reduce the use of third libraries

As the business changes frequently and complexity increases, we tend to use third-party Libaray, and sometimes we may only use a small number of features, so we need to be careful about fully referencing. Speaking from my development experience, I would rather refer to my own implementation than introduce a third party library.

  • Avoid the enumeration

An enumeration can add about 1.0 to 1.4 KB to your application’s classes.dex file. These additions can quickly accumulate to complex systems or shared libraries. If possible, consider using the @intdef annotation, which preserves all the type-safety benefits of enumerations.

  • Using ProGuard

The following code from the build.gradle file is used to enable code compression for release builds:

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

In addition to the minifyEnabled attribute, there is the proguardFiles attribute used to define ProGuard rules:

The getDefaultProguardFile(‘ proGuard-android.txt ‘) method obtains the default ProGuard Settings from the Android SDK tools/ proGuard/folder.

Tip: For further code compression, try using the proguard-Android-optimize.txt file in the same location. It includes the same ProGuard rules, but also other optimizations that perform analysis at the bytecode level (within and between methods) to further reduce APK size and help speed it up.

The proGuard-rules. pro file is used to add user-defined ProGuard rules. By default, this file is located in the module root (next to the build.gradle file).

Optimize the resource files in assets and RES

digression

Similarities between Res/Raw and Assets:

Files in both directories are stored intact in the APK package after packaging and will not be compiled into binary.

Differences between RES/Raw and Assets:

  1. Files in res/raw are mapped to the r.java file, which is accessed using the resource ID r.i.d.filename. Files in assets will not be mapped to R.Java and will be accessed using the AssetManager class.
  2. Res /raw cannot have a directory structure, while assets can have a directory structure. That is, folders can be created under assets.
  3. For different situations, there are different optimization strategies for resource files. In general, PNG resources in RES /drawable-** DDPI can be compressed.

3.1 Image resource optimization strategy

Format compression; Use TinyPng or Guetzli for compression.

Use WebP file format

When targeting Android 3.2 (API level 13) or higher, you can also use the WebP file format to make images instead of PNG or JPEG files. WebP formats provide lossy compression (such as JPEG) as well as transparency (such as PNG), but can provide better compression than JPEG or PNG.

Android 4.0 (API Level 14) supports lossy compression of WebP format, Android 4.3 (API Level 18) began to support lossless transparent WebP images.

See below:



High compression efficiency, only 12% of PNG format. Surprise no surprise…

Using vector graphics

You can use vector graphics to create resolution-independent ICONS and other scalable media. Using these graphics can greatly reduce your APK footprint. Vector images are represented as VectorDrawable objects in Android. With a VectorDrawable object, a 100-byte file can produce a sharp image that matches the screen size.

However, it takes a long time for the system to render each VectorDrawable object, and larger images take longer to appear on the screen. Therefore, these vector graphics are only considered when displaying small images.

Other strategies

Sometimes we may reuse an image. For example, setColorFilter or TINt can be used to transform the overall color of an image. Minimize the use of frame animation, which is a lot of pictures.

3.2 Compressing Resources

To enable resource compression, set the shrinkResources property to true in the build.gradle file.

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

The resource compressor does not currently remove resources (such as strings, sizes, styles, and colors) defined in the Values/folder. This is because the Android Resource Packaging tool (AAPT) does not allow the Gradle plugin to specify predefined versions of resources.

We can also specify which resources can be retained.

For example, save the following code in res/raw/keep.xml. The build does not package this file into APK.

<?xml version="1.0" encoding="utf-8"? >
<resources xmlns:tools="http://schemas.android.com/tools"
 tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
 tools:discard="@layout/unused2" />

Copy the code

Resources have the following attributes:

  • Tools :keep indicates which resources will be retained
  • Tools :discard Specifies which resources to discard
  • Tools :shrinkMode Indicates the resource compression mode, which can be “strict” or “safe”. The default value is “safe”

Safe and Strict optimizations:

Safe is simply known as safe mode, and does its best to check for resources that might be used in your code and reserve them to avoid runtime errors.

If your code calls resources.getidentifier (), this means that your code will query the resource name based on a dynamically generated string. When you perform this call, by default the resource compressor acts defensively, marking all resources with a matching name format as possibly in use and cannot be removed.

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

Copy the code

Img_ prefix resources are marked as used.

In strict mode, img_ prefix resources are treated as unused, so you need to use tools:keep to manually identify used resources.

Remove unused spare resources

We know that Google provides internationalization support for our APK, such as drawable resources for different screen resolutions and strings for different languages, but in many cases we just need resources that specify the resolution and language. At this point we can use the resConfigs method to configure.

defaultConfig {
 // For internationalization support, only Chinese resources are packaged.
 resConfigs "zh-rCN"
}

Copy the code

4. Resource optimization in lib

Here we will focus on the optimization strategy of dynamically linked libraries in Lib, also known as SO files. It may be easier to understand if you have experience with NDK development.

To support different instruction sets, applications may include armeabi, Armeabi-v7a, x86 SO files, and SO on.

Armeabi-v7a is compatible with armeabi-V7A. So for general development we only need to use Armeabi-v7a for ABI support.

Some SO libraries can be downloaded from the web, putting the burden on users after they install the application. Which SO files can be loaded on the network depends on the specific service situation.

As an aside, the application will crash if SO is not found at runtime.

java.lang.UnsatisfiedLinkError: Couldn't load stlport_shared
 from loader dalvik.system.PathClassLoader: findLibrary returned null
at java.lang.Runtime.loadLibrary(Runtime.java:365)
at java.lang.System.loadLibrary(System.java:535)
at com.your.app.NativeClass.<clinit>(Native.java:16)
... 63 more
Caused by: java.lang.UnsatisfiedLinkError: Library stlport_shared not found
at java.lang.Runtime.loadLibrary(Runtime.java:461)
at java.lang.System.loadLibrary(System.java:557)
at com.your.app.NativeClass.<clinit>(Native.java:16)
... 5 more

Copy the code

But there are ways to deal with it, see this open source project; ReLinker


Five, all-round performance tuning, Android architect brain map, full set of videos

5.1. Comprehensive performance tuning technical system;



5.2.Android Architect brain Map;




5.3. Full set of advanced architecture videos;

I’m putting together a complete set of Android architecture videos to share for free. It is still in the update, welcome to pay attention to thank you for your support

(Including Java fundamentals and principles, custom controls, NDK, architecture design, Hybrid development (Weex), performance optimization, complete commercial project development and other technical systems)