Android development performance optimization dry; let your APK thin 88%

Written at the beginning;

With the increasing complexity of the business, code, resource is constantly increasing, then you are also increasing the size of APP. From a user perspective, the face at every turn tens of megabytes of APP is in the non-WIFI cases still hesitant to download, no download you may lose a user. From the company level is concerned, traffic is money, reduce the size of APP is particularly important. From the developer terms, you have mastered this craft will be slightly grid full force.

Ado, start the topic.

Those things 1.APK structure

Know thyself, can only know yourself. Understand the structure of the application APK helpful for us. APK file consists of a ZIP archive composition, which 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 file and MANIFEST.MF manifest file.
  • assets /: contains the application can use the application resource AssetManager search target.
  • res /: contains not compiled into a resource resources.arsc.
  • lib /: processor-specific software layers comprising a compiled code. This directory contains a subdirectory for each platform, like armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, and mips.
  • resources.arsc: containing the compiled resources. This file contains the res / values ​​/ folder contents of all XML configuration. Packaging tool to extract the XML content will be compiled into a binary format and content archiving. This includes language strings and style, and the content contained in the direct path resources.arsc file, for example, and image layout file.
  • classes.dex: contains Dalvik / ART virtual machine file format understandable DEX compiled classes.
  • AndroidManifest.xml: Android manifest file contains the core. The document lists the application name, version, and access to reference libraries. The binary file using the Android XML format.
  • Take a look at the file directory of APP after Taobao unzip

In general, APK structure is a relatively large part of the general classes.dex, lib, res, assets such file or directory. So the next will explain for these four cases.

In addition, we can analyze by APK APK Analyser

2. Reduce classes.dex

classes.dex contains all the Java code. When you compile your application, gradle will convert all of your modules in the .class file into .dex files and those files into one file classes.dex.

Classes.dex single file can hold about 64K method. 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 method. So classes.dex determined by the number of files you count method.

Reduce the use of third library

With frequent changes and increasing complexity of the business, we tend to use third-party Libaray, sometimes we may only use a small part of the function, this time need to be carefully considered complete reference. From my experience in terms of development, preferring to refer themselves to achieve, but also we do not want to introduce a third-party library.

Avoid Enumeration

An enumeration can add about 1.0 to 1.4 KB in size classes.dex file your application. These additions can quickly accumulate to a complex system or a shared library. If possible, consider using @IntDef notes, this type of conversion retains all the advantages of type-safe enumeration.

Use ProGuard

The following code from build.gradle file for code compression is enabled for the release build:

 

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

In addition minifyEnabled properties, as well as the rules for defining ProGuard proguardFiles properties:

getDefaultProguardFile ( 'proguard-android.txt') method can be obtained from the default setting ProGuard Android SDK tools / proguard / folder.

提示:要想做进一步的代码压缩,请尝试使用位于同一位置的 proguard-android-optimize.txt 文件。它包括相同的 ProGuard 规则,但还包括其他在字节码一级(方法内和方法间)执行分析的优化,以进一步减小 APK 大小和帮助提高其运行速度。

proguard-rules.pro 文件用于添加自定义 ProGuard 规则。默认情况下,该文件位于模块根目录(build.gradle 文件旁)。

3.优化assets和res中的资源文件

题外话

res/raw和assets的相同点:

两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。

res/raw和assets的不同点:

  1. res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
  2. res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹。
  3. 针对不同的情况,对于资源文件有不同的优化策略。一般来讲,对于res/drawable-**ddpi中的png资源可以进行压缩。

3.1 图片资源优化策略

格式压缩

使用TinyPng或者Guetzli进行压缩。

使用WebP文件格式

定位Android 3.2(API级别13)或更高级别时 ,您也可以使用WebP文件格式来制作图像,而不是使用PNG或JPEG文件。WebP格式提供有损压缩(如JPEG)以及透明度(如PNG),但可以提供比JPEG或PNG更好的压缩。

Android 4.0 (API level 14) 支持有损压缩的WebP格式,Android 4.3 (API level 18) 开始支持无损透明WebP图像。

看下图:

 

压缩效率极高,仅为PNG格式的12%。惊喜不惊喜。。。

使用矢量图形

您可以使用矢量图形来创建与分辨率无关的图标和其他可伸缩媒体。使用这些图形可以大大减少您的APK足迹。矢量图像在Android中表示为VectorDrawable对象。通过一个VectorDrawable对象,一个100字节的文件可以生成一个与屏幕尺寸一致的清晰图像。

但是,系统渲染每个 VectorDrawable对象需要很长时间,而较大的图像需要更长的时间才能显示在屏幕上。因此,只有在显示小图像时才考虑使用这些矢量图形。

其它策略

有时候我们可能对一张图片进行重复利用,比如一张图片仅仅是整体颜色的变换可以使用setColorFilter或者tint。尽量减少使用帧动画,那可是一堆图片呀。

3.2 压缩资源

要启用资源压缩,请在 build.gradle 文件中将 shrinkResources 属性设置为 true。

 

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

资源压缩器目前不会移除 values/ 文件夹中定义的资源(例如字符串、尺寸、样式和颜色)。这是因为 Android 资源打包工具 (AAPT) 不允许 Gradle 插件为资源指定预定义版本。

同时,我们也可以指定哪些资源可以保留下来。

例如,将下边的代码保存在 res/raw/keep.xml。构建不会将该文件打包到 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" />

resources有以下属性:

  • tools:keep 指出哪些资源会保留
  • tools:discard 指定哪些资源需要剔除
  • tools:shrinkMode 资源压缩模式,有两种取值strict和safe,默认为safe

safe和strict的优化策略:

safe可以简单理解为安全模式,它会尽最大努力检查代码中可能会使用到的资源进行保留,避免运行时错误。

如果你的代码调用 Resources.getIdentifier(),这就表示你的代码将根据动态生成的字符串查询资源名称。当你执行这一调用时,默认情况下资源压缩器会采取防御性行为,将所有具有匹配名称格式的资源标记为可能已使用,无法移除。

 

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

img_ 前缀的资源标记为已使用。

在strict模式下,img_前缀的资源会做未使用的处理,因此你需要使用tools:keep手动进行已使用标识。

移除未使用的备用资源

我们知道google给我们的apk提供了国际化支持,如适应不同的屏幕分辨率的drawable资源,还有适应不同语言的字符串资源等等,但是在很多情况下我们只需要一些指定分辨率和语言的资源就可以了,这个时候我们可以使用resConfigs方法来配置。

 

defaultConfig {
 // 对于国际化支持只打包中文资源,
 resConfigs "zh-rCN"
}

4.lib中资源优化

这里我们主要讲一下lib中动态链接库的优化策略,也就是SO文件。如果你有NDK的开发经验可能会更容易理解一些。

为了支持不同指令集的情况,应用可能会包含armeabi、armeabi-v7a、x86的SO文件等。

目前主流的机型都是支持armeabi-v7a的,并且armeabi-v7a兼容armeabi。所以在一般的开发中我们只需要使用armeabi-v7a 进行ABI支持。

有些SO库可以采用网络下载,把负担放到用户安装完应用之后。对于哪些SO文件可以放到网络中加载,还需要看具体业务情况。

题外话,如果运行时找不到SO的话,会导致应用崩溃。

 

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

我们也是有办法应对的,可以参见这个开源项目ReLinker

本文的Android安装包大小优化知识到此结束

 

写在最后;

作者目前在深圳,13年java转Android开发,在小厂待过,也去过华为,OPPO等,去年四月份进了阿里一直到现在。后面转管理后也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效,原理源码确实容易头疼,造成很多开发者工作几年后就陷入瓶颈难以往上突破。

 

我去年开始,整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,华为,小米,等一线互联网公司主流架构技术。

【阿里P7级】Android高级进阶技术脑图

(大家如果有想往上提升技术,建议先搭建一个全面的技术框架,然后再深挖)

 

 

 

也可以关注我的;Android高级进阶专栏。里面有很多在BAT等一线大厂待过,或者在职的技术大佬。哪怕只关注看看不学,也能一定程度上提升思维和格局。

欢迎关注和学习交流~

发布了17 篇原创文章 · 获赞 0 · 访问量 177

Guess you like

Origin blog.csdn.net/chuhe1989/article/details/104266708