The ultimate means of Android package volume optimization

preface

There is no need to say more about the importance of package size. Package size directly affects users' downloading, retention, and even some manufacturers' pre installed mandatory requirements must be less than a certain value. However, with the iterative development of the business, the application will become larger and larger, and the installation package will keep expanding. Therefore, the reduction of package size is a long-term and continuous governance process.

  • Improve the download conversion rate. The smaller the installation package, the higher the conversion rate.
  • Reduce the cost of channel promotion.
  • Reduce installation time, file copy, Library decompression, ODEX compilation and signature verification. The larger the package size, the more time-consuming.
  • Reduce runtime memory, etc.

environment

  • Android Studio Arctic Fox | 2020.3.1 Patch 2
  • AGP 7.0
  • Project address: Wan Android_ jetpack

Before optimization

  • 4.7MB and 4.2MB are the download size of google play, which will be compressed.

In addition to the Analyzer provided by AS, there are ApkChecker, classysark and other tools.

Composition of APK

APK construction process

This is the official packaging process of the new version. Although some steps are omitted, the general process is relatively clear.

Simplify again:

Resource files Java file > dex file > APK

Optimization ideas

APK is essentially a compressed file and a product after packaging. The stages that can be used as the entry point are before and during packaging.

  • Before packaging, reduce the packaged files, such as useless resources and code;
  • During packaging, the products in packaging are compressed, such as resource files and So files;

Key words: reduction, compression.

General operation

1.Lint detects useless resource files

Analyze > Run Inspection by Name > Unused resources

Test results:

OK, delete it.

Note: because lint is a local static scan, dynamically referenced resource files will not be recognized and will also appear in the detection list.

2.Lint test code

Analyze > Inspect code 

Test results:

Because this project is written in kotlin, you can directly see the test results under the kotlin directory.

Note: because lint is a local static scan, reflected and dynamically referenced class es will not be recognized and will also appear in the detection list.

3. Picture compression

tinypng online compression is recommended.

4.TinyPngPlugin

Manual compression is not efficient after all. You can use TinyPngPlugin one click compression.

plugins search for TinyPng installation. (there is no need to restart the plugin after installing the new version of AS)

Compression results:

9 pictures, you can see the effect is still very impressive. If there are many pictures, the effect is more obvious.

After the above operation, the packet volume is reduced by 4%, which is just a 4.7MB APK.

5.WebP

Can these 9 pictures continue to be optimized? Yes, the volume of WebP format is smaller, and AS also provides one click conversion support.

With ic_avatar.png as an example:

ic_avatar.png optimized original size 113.09KBTingPng compressed 36.85kbwebp8 66KB

It can be seen that after switching to WebP, the original size is reduced by nearly 93%~

6. Turn on confusion

minifyEnabled true. R8 code reduction is enabled by default.

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

Use R8 with caution because:

R8 ignores all ProGuard rules that attempt to modify the default optimization behavior, such as - optimizations and - optimizationpasses.

Confusion can be turned on without using R8.

android.enableR8=false
android.enableR8.libraries=false

Confusion reference: Android confusion from entry to mastery

7. Resource reduction

shrinkResources true

If some resource files are not sure whether they are still in use or not, and dare not delete them, or whether the requirements will change, so keep them first, what should we do in this case? Shrink resources can be used to shrink resources.

    buildTypes {
        debug {
            minifyEnabled false
        }
        release {
            shrinkResources true
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

It should be used together with minifyEnabled. The principle is also very simple. After the code is removed, the referenced resources will become useless resources and can be further reduced.

8.so file reduction

For example, if a three-party live broadcast or browser is integrated, many so files may be provided. At first, it may be a brain copy into the project, but not all of them can be used.

For example, so of Various cpu architectures:

app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── arm64-v8a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── x86/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
└── x86_64/
    ├── libgameengine.so
    ├── libothercode.so
    └── libvideocodec.so

At present, the mobile phone CPUs on the market are all based on ARM architecture, so just keep one of arm (except customized ones), and either armeabi-v7a or armeabi can be deleted directly.

android {
    defaultConfig {
        ndk {
            abiFilters 'armeabi-v7a'
        }
    }
}

If the development needs simulator debugging, add the x86 architecture, and remember to remove the formal package, or in local Use variables in properties to control it.

If this piece has not been optimized before and there are many so files, it may be reduced by more than 30%. It's terrible!

9. Remove unused spare resources

Many overseas applications will be internationalized, but they can't adapt to so many languages. In addition to their own apps, there are also some official and third-party languages that can be configured uniformly.

    defaultConfig {        resConfigs("en","zh","zh-rCN")    }

The same is true for resource files

    defaultConfig {        resConfigs("xxhdpi","xxxhdpi")    }

10. Summary

Make a summary of the above operations to see the current effect.

2MB, the package volume is reduced by 57%, so terrible!

If it is a large project, the income is very considerable.

author: yechaoa

Advanced operation

The above is just some general operations. Let's look at some advanced operations.

1.resources.arsc resource confusion

Resource obfuscation is to shorten the originally lengthy resource path, such as changing res/drawable/wechat to r/d/a. Open source tool AndResGuard.

2. Remove useless third-party libraries

Not used after the introduction, or not removed after the function is removed from the shelf.

3. Tripartite library integration with repeated functions

For example, glass and picasso are both picture libraries. Just keep one of them.

4.ReDex

dex file is a product of packaging, and REDEX is an open source subcontracting optimization scheme of facebook. Refer to: REDEX.

5.so dynamic loading

The so files have been reduced previously, but the proportion of so files may still be large. We can consider dynamic distribution of so files except for the first startup. That is, the idea of plug-in, loading on demand, but with great benefits, there are also great risks. There are many case s to consider, such as download timing, network environment, thread process, whether there is a degradation strategy for loading failure, etc.

You can refer to facebook's open source SoLoader.

6. Plug in

Load on demand. The greater the income, the greater the risk. The risk is the same as above.

Extreme operation

If I want to be the best, what else can I do? ok, continue.

1. Switch to H5 or applet programs

Some functions may be too heavy to be native. For example, various promotional activities need to load various large pictures. Native functions are heavy and not dynamic enough. H5 is a good alternative at this time. However, if you do not support H5 or applet originally, accessing this capability may increase the packet volume and make a comparison.

2. Hacking function

Some functions may think very well, but the revenue is not large after going online. Whether it is necessary to rethink the value points, it is best to find data support and fight with the products.

3. Modify the source code of the third-party library and eliminate unnecessary code

For example, a fully functional third-party library utils is introduced, but it actually takes only a few. Extracting the source code can also reduce the package volume and reduce the compilation time of network download.

The disadvantage is that the upgrade cost is large.

4. Picture networking

That is, upload the picture to the server and reduce the package volume through dynamic download. The disadvantage is that the first loading depends on the network environment and needs to balance the loading speed and traffic. Images can be preloaded, but traffic consumption cannot be avoided. If you care about traffic indicators, you need to weigh them.

5.DebugItem

DebugItem mainly contains two kinds of information:

  • Debug information. Parameter variables and all local variables of the function.
  • Troubleshooting information. The correspondence between all instruction set line numbers and source file line numbers.

Remove the debug information and line number information. If it is not extreme, it is not recommended. You can refer to Alipay's Alipay App building optimization analysis: Android packet size is the most extreme compression.

6.R Field inline

Inline R Field can solve the problem of MultiDex 65536 caused by too many R fields, and this step can have an obvious effect on code slimming.

Meituan code snippet:

ctBehaviors.each { CtBehavior ctBehavior ->
    if (!ctBehavior.isEmpty()) {
        try {
            ctBehavior.instrument(new ExprEditor() {
                @Override
                public void edit(FieldAccess f) {
                    try {
                        def fieldClassName = JavassistUtils.getClassNameFromCtClass(f.getCtClass())
                        if (shouldInlineRField(className, fieldClassName) && f.isReader()) {
                            def temp = fieldClassName.substring(fieldClassName.indexOf(ANDROID_RESOURCE_R_FLAG) + ANDROID_RESOURCE_R_FLAG.length())
                            def fieldName = f.fieldName
                            def key = "${temp}.${fieldName}"

                            if (resourceSymbols.containsKey(key)) {
                                Object obj = resourceSymbols.get(key)
                                try {
                                    if (obj instanceof Integer) {
                                        int value = ((Integer) obj).intValue()
                                        f.replace("\$_=${value};")
                                    } else if (obj instanceof Integer[]) {
                                        def obj2 = ((Integer[]) obj)
                                        StringBuilder stringBuilder = new StringBuilder()
                                        for (int index = 0; index < obj2.length; ++index) {
                                            stringBuilder.append(obj2[index].intValue())
                                            if (index != obj2.length - 1) {
                                                stringBuilder.append(",")
                                            }
                                        }
                                        f.replace("\$_ = new int[]{${stringBuilder.toString()}};")
                                    } else {
                                        throw new GradleException("Unknown ResourceSymbols Type!")
                                    }
                                } catch (NotFoundException e) {
                                    throw new GradleException(e.message)
                                } catch (CannotCompileException e) {
                                    throw new GradleException(e.message)
                                }
                            } else {
                                throw new GradleException("******** InlineRFieldTask unprocessed ${className}, ${fieldClassName}, ${f.fieldName}, ${key}")
                            }
                        }
                    } catch (NotFoundException e) {
                    }
                }
            })
        } catch (CannotCompileException e) {
        }
    }
}

At the same time, you can refer to the byte open source shrink-r-plugin and didi open source booster.

7. Picture shader

For the processing of different colors in the same image, you can use tint. For example, when a black return icon is used, now another page needs to use white, so you don't need two images, but use tint to modify it to white.

  <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/ic_back_black"
                android:tint="@android:color/white" />

8. Reduce the use of ENUM

Each reduction of ENUM can reduce the size by about 1.0 to 1.4 KB.

Package volume monitoring

Package volume monitoring should be a part of the release process. It is best to achieve platform and process, otherwise it is difficult to continue, and the package volume of few versions has risen again.

General idea: compare the package size of the current version with that of the previous version. If it exceeds 200KB, it needs to be approved. Subsequent optimization schemes need to be given for temporary approval.

last

Some time ago, I collected and sorted out the knowledge brain map of Android performance optimization system and the notes of core knowledge points! It can not only consolidate the core technical points such as underlying principles and performance tuning, but also master the architecture design methodology that ordinary developers can't touch. Then you have the core competitiveness that your peers can't copy in your work, team and interview. The full version has been sorted and uploaded to Public account: Android development home , you can visit it yourself.

The full version of Android performance optimization system knowledge brain map and core knowledge points note document has been sorted and uploaded to Public account: Android development home , you can visit it yourself.

Keywords: Android Design Pattern

Added by fri on Tue, 11 Jan 2022 16:30:21 +0200