Analogous to WeChat, how to compress Apk to the extreme, talk about the 8 steps of Android compression

Analogous to WeChat, how to compress Apk to the extreme, talk about the 8 steps of Android compression

Introduction

As the project continues to iterate, the amount of code and resource files continue to increase. Then the packaged APK file will become larger and larger. If your boss or leader suddenly asks you to optimize the APK size one day, and you still don’t know how to optimize it, it’s a bit unreasonable. In this article, let’s analyze and optimize the APK size together.

Analyze APK resource usage

Notice:

I found a popular open source project on GitHub. If you need it, you can click to download it and try it yourself.

AS Build/Analyze APK used directly by analysis tools

From the above figure, we can see that assets > classes.dex > res > lib, among which resource files occupy the largest space.

Now let's take a look at how to reduce the APK size.

Eight steps to optimize APK size

1. Convert the image to webp format

Webp Concept

WebP is an image file format that provides both lossy and lossless compression, derived from the video encoding format VP8. WebP was first released in 2010 with the goal of reducing file size while achieving the same image quality as JPEG, hoping to reduce the time it takes to send image files over the Internet. On November 8, 2011, Google began to support lossless compression and transparent color in WebP.

According to earlier tests by Google, WebP's lossless compression reduces the file size by 45% compared to PNG files found on the web. Even if these PNG files are processed using PNGCRUSH and PNGOUT, WebP can still reduce the file size by 28%. For now, WebP can reduce the image size by an average of 70%. WebP is the future development trend of image formats.

PNG / JPG to Webp

Right-click the image or folder and select Convert to Webp format to compress the png/jpg image into webp format.

In the end, we only reduced it by less than 200 kb. It is possible that the project image resources were not very large, but there were too many small images.

Application scenarios and advantages

  • The client software has a built-in Chromium-based webview. The web pages used in this type of browser can fully use the WebP format to improve the loading and rendering speed without considering compatibility.
  • For programs developed with node-webkit, using WebP can reduce the size of the file package.
  • Mobile applications or web games require a large number of images in their interfaces. WebP decoding packages can be embedded to save user traffic and improve access speed. Advantages:
  • For PNG images, WebP is 45% smaller than PNG.

2. Remove multilingual

Add in app/build.gradle

  1. android{
  2. ...
  3. defaultConfig{
  4. ...
  5. //Only keep English
  6. resConfigs "en"  
  7. }
  8. }

Here we found a reduction of about 200 kb

3. Remove unnecessary so libraries

By decompiling the Android WeChat version, we know that WeChat is only adapted to the armeabi-v7a architecture, so let's delete the support of other libraries.

  1. android{
  2. ...
  3. defaultConfig{
  4. ...
  5. ndk {
  6. //Set the supported SO library architecture
  7. abiFilters "armeabi-v7a"  
  8. }
  9. }
  10. }


Optimized about 600 kb more, continue.

4. Remove useless resources Link check (delete with caution)

concept

Lint is a code scanning and analysis tool provided by Android Studio. It can help us find code structure/quality problems and provide some solutions. This process does not require us to write test cases manually. When there are many code iterations, it is easy to leave some useless code and resource files. We can use Lint to clear them.

How to use Link Check

Open the AS tool and go to Analyze > Run Inspection By Name > unused resources

optimization

We found that our link was optimized by about 700 kb.

Notice

Because link checks whether there are references to determine whether the resource is used, if this is the case, you must be careful when deleting it.

  1. //Dynamically obtain resource id. If R.xx.xx is not used directly, the resource represented by this id will be considered unused (similar to not being able to confuse reflection classes)
  2. int indetifier =getResources().getIdentifier( "img_bubble_receive" , "drawable" , getPackageName()); getResources().getDrawable(indetifier);

5. Turn on obfuscation

Optimized about 1.7M and continued.

6. Remove useless resources shinkResource

Enable shinkResource = true

  1. buildTypes {
  2. release {
  3. minifyEnabled true  
  4. shrinkResources = true  
  5. proguardFiles getDefaultProguardFile( 'proguard-android.txt' ), 'proguard-rules.pro'  
  6. }
  7. debug {
  8. shrinkResources = true  
  9. minifyEnabled true  
  10. proguardFiles getDefaultProguardFile( 'proguard-android.txt' ), 'proguard-rules.pro'  
  11. }
  12. }

This may be because the link deleted useless resources, so it is not optimized.

7. Enable deleting useless resources (strict mode and normal mode) - I cannot test this here, but you can test the effect later.

Normal mode is also called custom mode

If you have specific resources that you want to keep or discard, create an XML file in your project that contains tags and specify each resource to keep in the tools:keep attribute and each resource to discard in the tools:discard attribute. Both attributes accept a comma-delimited list of resource names. You can use the asterisk character as a wildcard.

For example:

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

Save the file in your project resources, for example, in res/raw/keep.xml. The build does not package the file into the APK.

Specifying resources to discard might seem silly since you could just remove them, but it can be useful when using build variants. For example, if you know that a given resource will ostensibly be used in your code (and therefore not be removed by the shrinker) but will not actually be used in a given build variant, you can put all resources in a common project directory and create a different keep.xml file for each build variant. Build tools may also not correctly identify resources as needed because the compiler adds inline resource IDs and resource analyzers may not know the difference between a truly referenced resource and an integer value in the code that happens to have the same value.

Strict Mode

Normally, the resource shrinker can accurately determine whether a resource is used by the system. However, if your code calls Resources.getIdentifier() (or any of your libraries make this call - the AppCompat library does), this means that your code is looking up a resource name based on a dynamically generated string. When you make this call, by default the resource shrinker takes defensive action and marks all resources with matching name formats as potentially used and cannot be removed.

For example, the following code will mark all resources prefixed with img_ as used.

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

The resource shrinker also looks through all string constants in your code and various res/raw/ resources, looking for resource URLs of a format similar to file:///android_res/drawable//ic_plus_anim_016.png If it finds strings like that, or other strings that look like they could be used to construct URLs like that, it does not remove them.

These are examples of safe shrinking mode, which is enabled by default. However, you can disable this "safety first" approach and instruct the resource shrinker to only keep resources that it is sure are used. To do this, set shrinkMode to strict in the keep.xml file, as shown below:

  1. <?xml version= "1.0" encoding= "utf-8" ?>
  2. <resources xmlns:tools= "http://schemas.android.com/tools"  
  3. tools:shrinkMode= "strict" />

If you do enable strict compression mode, and your code also references resources that contain dynamically generated strings (as shown above), you must manually preserve those resources using the tools:keep attribute.

8. AndResGuard WeChat resource compression solution

What is AndResGuard

AndResGuard is a tool to reduce the size of APK. Its principle is similar to Java Proguard, but it only targets resources. It will shorten the original lengthy resource path, for example, res/drawable/wechat will be changed to r/d/a.

Why use AndResGuard

In previous development, we usually only obfuscated the code, but the resource files were exposed to others, and all the file names under the res folder were too readable.

Effect after use

AndResGuard Configuration

In build.gradle in the project root directory, add the plugin dependency:

  1. dependencies {
  2. classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.16'  
  3. }

In the app directory, create the and_res_guard.gradle file.

  1. apply plugin: 'AndResGuard'  
  2. andResGuard {
  3. mappingFile = null  
  4. use7zip = true  
  5. useSign = true  
  6. keepRoot = false  
  7. compressFilePattern = [
  8. "*.png" ,
  9. "*.jpg" ,
  10. "*.jpeg" ,
  11. "*.gif" ,
  12. "resources.arsc"  
  13. ]
  14. whiteList = [
  15. //your icon
  16. "R.drawable.icon" ,
  17. // for fabric
  18. "R.string.com.crashlytics.*" ,
  19. // for umeng update  
  20. "R.string.tb_*" ,
  21. "R.layout.tb_*" ,
  22. "R.drawable.tb_*" ,
  23. "R.drawable.u1*" ,
  24. "R.drawable.u2*" ,
  25. "R.color.tb_*" ,
  26. // umeng share for sina
  27. "R.drawable.sina*" ,
  28. // for google-services.json
  29. "R.string.google_app_id" ,
  30. "R.string.gcm_defaultSenderId" ,
  31. "R.string.default_web_client_id" ,
  32. "R.string.ga_trackingId" ,
  33. "R.string.firebase_database_url" ,
  34. "R.string.google_api_key" ,
  35. "R.string.google_crash_reporting_api_key" ,
  36. //Friends Alliance
  37. "R.string.umeng*" ,
  38. "R.string.UM*" ,
  39. "R.layout.umeng*" ,
  40. "R.drawable.umeng*" ,
  41. "R.id.umeng*" ,
  42. "R.anim.umeng*" ,
  43. "R.color.umeng*" ,
  44. "R.style.*UM*" ,
  45. "R.style.umeng*" ,
  46. // RongCloud
  47. "R.drawable.u*" ,
  48. "R.drawable.rc_*" ,
  49. "R.string.rc_*" ,
  50. "R.layout.rc_*" ,
  51. "R.color.rc_*" ,
  52. "R.id.rc_*" ,
  53. "R.style.rc_*" ,
  54. "R.dimen.rc_*" ,
  55. "R.array.rc_*"  
  56. ]
  57. sevenzip
  58. artifact = 'com.tencent.mm:SevenZip:1.2.10'  
  59. }
  60. }

Add to the build.gradle file under the app module:

  1. apply from : 'and_res_guard.gradle'  

Effect picture after packaging

The resource is compressed by about 1M

Summarize

The larger the project and the more resources it has, the more obvious the effect will be.

If you use Link to delete resources, you must be cautious and make backups in advance.

Since the project itself is only 22 MB, we finally optimized it down to 4.5 MB. It is still not easy.

<<:  Google officially mentions Android 11 for the first time

>>:  APP interface layout "little experience"

Recommend

Marketing promotion plan: New media marketing hot calendar in May!

Whether it is new media, marketing, event plannin...

Are you laughing at Erha? He has been to the Olympics!

When people mention Huskies, they always associat...

Practical application of 6 short video content models!

The practical tutorials for the last three short ...

Introduction to Huawei AppGallery's first release rules!

Introduction to application launch rules The firs...

It’s the last day of the first lunar month, and I can finally get a haircut?

Mixed Knowledge Specially designed to cure confus...

Tips and strategies for becoming a “super user”!

The free feast on the Internet is becoming a thin...

Home central air conditioning is a tough nut to crack

From the perspective of home air conditioner manu...

Beginner's guide to UI design: iOS

This article is the first chapter of UI design, m...

SEO workflow, what are the specific tasks of SEO every day?

SEO optimization is a boring job. Generally, a ne...