[TOC]
参见官方文档:[https://developer.android.com/topic/performance/reduce-apk-size?hl=zh-cn](https://developer.android.com/topic/performance/reduce-apk-size?hl=zh-cn)
## 了解APK结构
![](https://img.kancloud.cn/97/65/9765ec02f7c6bef887f2c7cd1f583dac_951x609.png)
## 缩减APK大小
使用App Bundle上传应用不适用于国内环境,暂不介绍。App Bundle 针对不同用户的设备配置,提供经过优化的 APK,只需下载运行应用时所需的代码和资源,无需再编译、签署和管理多个 APK 以支持不同的设备。可以使用Android Size Analyzer插件或者命令行对项目进行分析,以确定哪些文件可以进行动态分发。
针对于国内的生态环境,我们主要从以下几个方面进行APK大小的优化:
### 缩减资源数量和大小
#### 1、移除不使用的资源文件
项目迭代过程中UI不断优化,部分资源文件不再使用,但仍存在于项目并被打包到APK中。可以使用`lint`工具来检测未被引用的资源文件。
>`lint`工具不会扫描`assets/`文件夹、通过反射引用的资源或已链接至应用的库文件。此外,它也不会移除资源,只会提醒您它们的存在。
同时,可以在应用的`build.gradle`文件中启用[`shrinkResources`](https://developer.android.com/studio/build/shrink-code?hl=zh-cn#shrink-resources),Gradle 会自动为我们移除资源。
```plain
android {
// Other settings
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
```
#### 2、减少封装库中的资源使用量
#### 3、仅支持特定屏幕密度
目前市场上手机屏幕分辩率以1080P为主流,推荐应用包含一个`xxhdpi`图片变体即可,低密度的`ldpi`、`mdpi`、`hdpi`等可以考虑移除,Android 会自动缩放最初为其他屏幕密度设计的现有资源。
#### 4、使用可绘制对象
对于可以使用Drawable对象动态绘制的图片,如背景图等,推荐使用Drawable对象替代,其占用的空间会比静态图片资源小。
#### 5、重复使用资源
对于一些可以重复使用的图片资源,推荐替换色调调整、阴影设置、旋转等,重复使用。以下示例展示了通过绕图片中心位置旋转 180 度,将“拇指向上”变为“拇指向下”::
```xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_thumb_up"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="180" />
```
#### 6、压缩PNG文件
`aapt`工具可以在编译过程中通过无损压缩来优化放置在`res/drawable/`中的图片资源。例如,`aapt`工具可以通过调色板将不需要超过 256 种颜色的真彩色 PNG 转换为 8 位 PNG。这样做会生成质量相同但内存占用量更小的图片。
使用注意:
* `aapt`工具不会缩减`asset/`文件夹中包含的 PNG 文件。
* 图片文件需要使用 256 种或更少的颜色才可供`aapt`工具进行优化。
* `aapt`工具可能会扩充已压缩的 PNG 文件。为防止出现这种情况,您可以在 Gradle 中使用`cruncherEnabled`标记为 PNG 文件停用此过程:
```plain
aaptOptions {
cruncherEnabled = false
}
```
#### 7、压缩PNG和JPEG文件
可以使用[pngcrush](http://pmt.sourceforge.net/pngcrush/)、[pngquant](https://pngquant.org/)或[zopflipng](https://github.com/google/zopfli)等工具缩减 PNG 文件的大小,同时不损失画质。所有这些工具都可以缩减 PNG 文件的大小,同时保持肉眼感知的画质不变。
`pngcrush`工具尤为有效:该工具会迭代 PNG 过滤器和 zlib (Deflate) 参数,使用过滤器和参数的每个组合来压缩图片。然后,它会选择可产生最小压缩输出的配置。
要压缩 JPEG 文件,可以使用[packJPG](http://www.elektronik.htw-aalen.de/packjpg/)和[guetzli](https://github.com/google/guetzli)等工具。
#### 8、使用 WebP 文件格式
使用[WebP](https://developers.google.com/speed/webp/?hl=zh-cn)文件格式的图片(而不是使用 PNG 或 JPEG 文件)。Android Studio提供了转换单张图片或整个目录为WebP格式的方法,具体可参考[https://developer.android.com/studio/write/convert-webp?hl=zh-cn](https://developer.android.com/studio/write/convert-webp?hl=zh-cn)
#### 9、使用矢量图形
可以使用矢量图形创建与分辨率无关的图标和其他可伸缩媒体。
#### 10、将矢量图形用于动画图片
请勿使用`[AnimationDrawable](https://developer.android.com/reference/android/graphics/drawable/AnimationDrawable?hl=zh-cn)`创建逐帧动画,这样做需要为动画的每个帧添加单独的位图文件,而这会大大增加 APK 的大小。
应改为使用[`AnimatedVectorDrawableCompat`](https://developer.android.com/reference/androidx/vectordrawable/graphics/drawable/AnimatedVectorDrawableCompat?hl=zh-cn)创建[动画矢量可绘制资源](https://developer.android.com/training/material/animations?hl=zh-cn#AnimVector)。
### 减少原生和Java代码
#### 1、移除不必要的生成代码
确保了解自动生成的任何代码所占用的空间。例如,许多协议缓冲区工具会生成过多的方法和类,这可能会使应用的大小增加一倍或两倍。
#### 2、避免使用枚举
单个枚举会使应用的`classes.dex`文件增加大约 1.0 到 1.4KB 的大小。这些增加的大小会快速累积,产生复杂的系统或共享库。如果可能,请考虑使用`@IntDef`注释和[代码缩减](https://developer.android.com/studio/build/shrink-code?hl=zh-cn)移除枚举并将它们转换为整数。此类型转换可保留枚举的各种安全优势。
#### 3、缩减原生二进制文件的大小
如果您的应用使用原生代码和 Android NDK,还可以通过优化代码来缩减发布版应用的大小。移除调试符号和不提取原生库是两项很实用的技术。
### 总结
综上可见,缩减APK大小主要从缩减资源文件和缩减原生及Java代码两方面入手,着重关注以下几点:
* 移除未使用的资源文件
* 使用可绘制对象
* 压缩图片文件,优先使用WebP格式文件
* 避免使用枚举
* 仅打包有需要平台的so文件
- 导读
- Java知识
- Java基本程序设计结构
- 【基础知识】Java基础
- 【源码分析】Okio
- 【源码分析】深入理解i++和++i
- 【专题分析】JVM与GC
- 【面试清单】Java基本程序设计结构
- 对象与类
- 【基础知识】对象与类
- 【专题分析】Java类加载过程
- 【面试清单】对象与类
- 泛型
- 【基础知识】泛型
- 【面试清单】泛型
- 集合
- 【基础知识】集合
- 【源码分析】SparseArray
- 【面试清单】集合
- 多线程
- 【基础知识】多线程
- 【源码分析】ThreadPoolExecutor源码分析
- 【专题分析】volatile关键字
- 【面试清单】多线程
- Java新特性
- 【专题分析】Lambda表达式
- 【专题分析】注解
- 【面试清单】Java新特性
- Effective Java笔记
- Android知识
- Activity
- 【基础知识】Activity
- 【专题分析】运行时权限
- 【专题分析】使用Intent打开三方应用
- 【源码分析】Activity的工作过程
- 【面试清单】Activity
- 架构组件
- 【专题分析】MVC、MVP与MVVM
- 【专题分析】数据绑定
- 【面试清单】架构组件
- 界面
- 【专题分析】自定义View
- 【专题分析】ImageView的ScaleType属性
- 【专题分析】ConstraintLayout 使用
- 【专题分析】搞懂点九图
- 【专题分析】Adapter
- 【源码分析】LayoutInflater
- 【源码分析】ViewStub
- 【源码分析】View三大流程
- 【源码分析】触摸事件分发机制
- 【源码分析】按键事件分发机制
- 【源码分析】Android窗口机制
- 【面试清单】界面
- 动画和过渡
- 【基础知识】动画和过渡
- 【面试清单】动画和过渡
- 图片和图形
- 【专题分析】图片加载
- 【面试清单】图片和图形
- 后台任务
- 应用数据和文件
- 基于网络的内容
- 多线程与多进程
- 【基础知识】多线程与多进程
- 【源码分析】Handler
- 【源码分析】AsyncTask
- 【专题分析】Service
- 【源码分析】Parcelable
- 【专题分析】Binder
- 【源码分析】Messenger
- 【面试清单】多线程与多进程
- 应用优化
- 【专题分析】布局优化
- 【专题分析】绘制优化
- 【专题分析】内存优化
- 【专题分析】启动优化
- 【专题分析】电池优化
- 【专题分析】包大小优化
- 【面试清单】应用优化
- Android新特性
- 【专题分析】状态栏、ActionBar和导航栏
- 【专题分析】应用图标、通知栏适配
- 【专题分析】Android新版本重要变更
- 【专题分析】唯一标识符的最佳做法
- 开源库源码分析
- 【源码分析】BaseRecyclerViewAdapterHelper
- 【源码分析】ButterKnife
- 【源码分析】Dagger2
- 【源码分析】EventBus3(一)
- 【源码分析】EventBus3(二)
- 【源码分析】Glide
- 【源码分析】OkHttp
- 【源码分析】Retrofit
- 其他知识
- Flutter
- 原生开发与跨平台开发
- 整体归纳
- 状态及状态管理
- 零碎知识点
- 添加Flutter到现有应用
- Git知识
- Git命令
- .gitignore文件
- 设计模式
- 创建型模式
- 结构型模式
- 行为型模式
- RxJava
- 基础
- Linux知识
- 环境变量
- Linux命令
- ADB命令
- 算法
- 常见数据结构及实现
- 数组
- 排序算法
- 链表
- 二叉树
- 栈和队列
- 算法时间复杂度
- 常见算法思想
- 其他技术
- 正则表达式
- 编码格式
- HTTP与HTTPS
- 【面试清单】其他知识
- 开发归纳
- Android零碎问题
- 其他零碎问题
- 开发思路