preface
The importance of package size goes without saying, package size directly affects users’ download, retention, and even some manufacturers’ pre-installation requirements must be less than a certain value. However, as the business develops iteratively, applications get bigger and packages get bigger, so package size reduction is a long-term governance process.
- Increase the download conversion rate. The smaller the package, the higher the conversion rate.
- Reduce channel promotion cost.
- Reduce installation time, file copy, Library decompression, ODEX compilation, signature verification, etc., the larger the package size, the more time consuming.
- Reduce runtime memory and so on.
The environment
- Android Studio Arctic Fox | 2020.3.1 Patch 2
- AGP 7.0
- Project address: wanAndroid_jetpack
Before optimization
4.7 MB
4.2MB is the size of Google Play download, there will be compression.
In addition to the Analyzer that comes with AS, there are tools such AS ApkChecker and ClassyShark.
The composition of the APK
file | describe |
---|---|
lib | So files, different CPU architectures |
res | Compiled resource files, drawable, layout, etc |
assets | Application resources, fonts, audio files, etc |
classes(n).dex | Dx compiled Java file |
META-INF | Signature Information Correlation |
resources.arsc | Binary resource file |
kotlin | The compiled kotlin file |
AndroidManifest.xml | The manifest file |
APK build process
This is the official version of the packaging process, although some steps are omitted, but the general process is relatively clear.
To simplify again:
Resource file, Java file > dex file > APKCopy the code
Optimization idea
APK is essentially a compressed file that is the product of packaging, and the stages that can be used as a starting point are before and during packaging.
- Before packaging, reduce the packaging files, such as useless resources, code;
- In packaging, compress the products in packaging, such as resource files and So files;
Key words: reduce, compress.
Normal operation
1.Lint detects useless resource files
Analyze > Run Inspection by Name > Unused resources
Copy the code
Test results:
Make sure it is useless to delete.
Note: Because Lint is a local static scan, dynamically referenced resource files are not recognized and will also appear in the checklist.
2.Lint checks code
Analyze > Inspect code
Copy the code
Test results:
Because the project is written in Kotlin, look directly at the test results in the Kotlin directory.
Note: Since Lint is a local static scan, reflected and dynamically referenced classes are not recognized and will also appear in the checklist.
3. Image compression
Tinypng is recommended for online compression.
4.TinyPngPlugin
After all, manual compression is not efficient, so TinyPngPlugin can be used for one-click compression.
Plugins search for TinyPng installation. (Plugin does not need to be restarted after the new VERSION of AS is installed)
Compression results:
Nine pictures, you can see the effect is still very impressive. If there are many pictures, the effect is more obvious.
This results in a 4% reduction in package size, and that’s just for a 4.7MB APK.
5.WebP
Can these 9 images be further optimized? Yes, the WebP format is smaller, but AS also provides one-click conversion support.
Take IC_avatar.png as an example:
ic_avatar.png | The optimized |
---|---|
Original size | 113.09 KB |
TingPng compression | 36.85 KB |
WebP | 8.66 KB |
It can be seen that after switching to WebP, the size has been reduced by nearly 93% compared to the original size
6. Turn on obfuscation
MinifyEnabled True. R8 code reduction is enabled by default.
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}Copy the code
Use R8 with caution because:
R8 ignores all ProGuard rules that attempt to modify default optimization behavior, such as -Optimizations and -OptimizationPasses.
Obfuscation can be turned on without R8.
android.enableR8=false
android.enableR8.libraries=false
Copy the code
Confusion Reference: Android confuses from beginner to master
7. Reduce resources
shrinkResources true
What if there are some resource files that you are not sure whether to use or delete, or you are not sure whether requirements will change, so you keep them? ShrinkResources can be used to shrinkResources.
buildTypes {
debug {
minifyEnabled false
}
release {
shrinkResources true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}Copy the code
It should be used in conjunction with minifyEnabled obfuscation, and the principle is very simple. After the code is removed, the referenced resource becomes useless 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, which may be copied into the project in a rush at the beginning, but not all of them can be used.
For example, so for various CPU architectures:
app/build/intermediates/cmake/universal/release/├── Heavy Exercises, ├─ heavy Exercises, ├─ heavy Exercises, ├─ heavy Exercises, heavy exercises, heavy exercises, heavy exercises, heavy exercises, heavy exercises, heavy exercises, heavy exercises, heavy exercises, heavy exercises, heavy exercises, heavy exercises Libgameengine. So │ ├ ─ ─ libothercode. So │ └ ─ ─ libvideocodec. So ├ ─ ─ x86 / │ ├ ─ ─ libgameengine. So │ ├ ─ ─ libothercode. So │ └ ─ ─ libvideocodec. So └ ─ ─ x86_64 / ├ ─ ─ libgameengine. So ├ ─ ─ libothercode. So └ ─ ─ libvideocodec. SoCopy the code
The current market of mobile CPU is arm architecture, so keep one of the ARM (except customized), ArmeabI-V7A or Armeabi can be used, other directly delete.
android {
defaultConfig {
ndk {
abiFilters 'armeabi-v7a'}}}Copy the code
If the development requires emulator debugging, add x86 architecture, remove the formal package, or use a variable in local.properties.
If this piece has not been optimized before, and there are many SO files, it may be reduced by more than 30%, so horrible!
9. Remove unused spare resources
A lot of apps out there are international, but they don’t work in so many languages. In addition to their own app, there are some official, tripartite, can be unified configuration support language.
defaultConfig {
resConfigs("en"."zh"."zh-rCN")}Copy the code
Same with resource files
defaultConfig {
resConfigs("xxhdpi"."xxxhdpi")}Copy the code
Nodule 10.
Make a summary of the above and see how it works so far.
2MB, 57% reduction in package size, horrible!
If it’s a big project, the benefits are huge.
Author: yechaoa
Advanced operation
This is just some general operations, but let’s look at some more advanced operations.
1. Resources. Arsc resource confusion
Resource obfuscating is shortening an otherwise lengthy resource path, such as changing res/drawable/wechat to R/D/A. Open source tool AndResGuard.
2. Remove useless tripartite libraries
Unused after introduction or not removed after feature removal.
3. Tripartite library integration with repeated functions
Glide and Picasso, for example, are both photo libraries. Keep either one.
4.ReDex
Dex files are packaged, and Redex is facebook’s open-source subcontracting optimization. For example: ReDex.
5. So dynamic loading
The so file has been reduced before, but the proportion of SO files may still be relatively large, so files can be dynamically delivered except for the first startup. That is, the idea of plug-in, loading on demand, but while the benefits are great, the risks are also great, there are many cases to consider, such as the download time, network environment, thread process, loading failure whether there is a downgrade strategy and so on.
See Facebook’s open source SoLoader.
6. The plugin
Load on demand, the greater the return, the greater the risk. Same as above.
Extreme operating
So if I want to do the best, what else can I do? Ok, go ahead.
1. Native switch to H5 or small program and other programs
Some features may be too heavy to do native, such as various promotional activities, which need to load various large pictures. Native features are both heavy and not dynamic enough, so H5 is a good alternative. But if you don’t already support H5 or applets, adding this capability might actually increase the package size for comparison.
2. Cut function
Some functions may be beautiful, but the revenue is not big after the launch. Do you need to rethink the value point? It is better to find the data and fight with the product.
3. Modify the source code of the tripartite library and eliminate unnecessary code
For example, a full-featured tripartite library utils was introduced, but only a few were actually used. Extracting source code also reduces package size and compilation time for network downloads.
The drawback is that the upgrade cost is high.
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 the loading speed and traffic need to be balanced. Images can be preloaded, but traffic consumption is unavoidable. If you care about traffic metrics, you need to weigh them.
5.DebugItem
The DebugItem contains two main types of information:
- Debug information. The parameter variables of the function and all local variables.
- Troubleshooting information. Mapping of all instruction set line numbers to source file line numbers.
Remove debugging information and line number information. Not recommended if it is not extreme. You can refer to this article alipay App construction optimization analysis: Android package size extreme compression.
6. R Field inline
Inline R fields can solve the problem of too many R fields causing MultiDex 65536, and this step can have an obvious effect on slimming code.
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) {
}
}
}
Copy the code
You can also refer to the Byte open source shrink-R-Plugin and the Didi open source Booster.
7. Image shaders
For the processing of different colors of the same image, tint can be used. For example, the original returned icon was black, and now another page is going to use white, so two images are not needed, but tint can be changed to white.
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_back_black"
android:tint="@android:color/white" />
Copy the code
8. Reduce the use of ENUM
Each reduction in ENUM reduces the size by approximately 1.0 to 1.4 KB.
Package volume monitoring
Package volume monitoring should be a part of the release process. It is better to be platform-based and process-based, otherwise it will be difficult to continue, and the volume of a few versions of the package will increase again.
General idea: The package size of the current version is compared with that of the previous version. If the package size exceeds 200KB, approval is required. Interim approval needs to give the follow-up optimization plan and so on.
Reference documentation
- Improve your code with lint checks
- Shrink, obfuscate, and optimize your app
- Android App package slimming optimization practice
- ReDex
- SoLoader
- Alipay App construction optimization analysis: Android package size extreme compression
- AndResGuard
- Explore Android package volume optimization in depth
- Android development master class volume optimization