Android 性能优化总结:包体积优化

前言

  随着开发不断迭代,App体积越来越大,包大小的增大也会给我们应用带来其他的影响 比如

  1. 下载率影响 过大的包体积会影响下载转化率,根据Google Play Store包体积和转化率分析报告显示,平均每增加1M,转化率下降0.2%左右
  2. 渠道限制 部分厂商预装强制要求安装包大小(比如国内市场在下载较大安装包就会提醒大流量是否继续下载的弹窗)
  3. 性能影响 过大的包体积在安装耗时和运行内存占用方面都会有很大影响

  但是包大小的优化不是一次就可以搞定的,需要持续的维护做好打持久战的准备,此篇文章算是在本地生活对包大小实战和别人的经验的总结,日常学习记录仅做参考。

基础了解

  在包体积优化前需要对APK做一个基本的了解

目录内容
lib文件夹下主要存放不同的cpu架构的so文件,会有armeabi、armeabi-v7a、arm64-v8a、x86、x86_64、mips,大多数情况下只需要支持armabi与x86的架构即可
res文件夹下存放编译后的资源文件,drawable和layout资源
assets应用程序的资源、字体、音频文件等,需要通过AssetManager类来访问内容
dexAndroid项目中的代码在编译后会生成.class文件,然后再通过dx工具转换为字节码文件,就是这个dex文件。一般情况下只有一个classes.dex,如果项目代码方法数超过了65535而采用了multidex的话,会有其他的.dex文件
META-INF签名信息,用来验证apk文件的完整性、合法性
resources.arsc二进制资源文件、AndroidManifest.xml清单文件

打包简要流程

image.png

打包主要有以下几步:

  1. 使用aapt工具处理所有的资源,生成一个R.java文件,一个resources.arsc文件以及其他资源。
  2. 处理.aidl文件,生成对应的Java接口文件。
  3. 将上述两步得到的R.java文件、Java接口文件,与Andorid源码一起,通过Java编译器,编译得到Java字节码文件.class文件。
  4. 获取依赖的第三方库文件,将其与上一步得到的.class文件一起,通过使用dx工具,生成.dex文件。
  5. 将资源索引文件resources.arsc、资源目录res、与上一步得到的.dex文件一起,通过apkbuilder工具,构建出初始的.apk文件。
  6. 使用jarsigner工具,对.apk文件进行签名。
  7. 使用zipalign工具,对.apk文件进行对齐。(让资源按四字节的边界进行对齐,加快资源的访问速度)

经过以上七步,一个完整的apk文件就诞生了。

那么针对压缩文件,主要的压缩体积方式分为:减少和压缩

包现状分析

使用AppChecker分析

  包体分析主要借助的是腾讯AppChecker完成的,AppChecker分析包文件主要还是借助了andoid build-tool下面的 aapt工具

image.png

  上图只是查看了各个文件类型占比,还支持统计 APK 中包含的 R 类、检查是否有多个动态库静态链接了 STL 、搜索 APK 中包含的无用资源、重复资源分析以及支持自定义检查规则等 (强烈推荐的检测工具)

借助AS提供的Analyze APK

image.png

  可以直观的查看APK的组成大小占比等信息,也可以用来查看其他产品使用了那些三方库等信息。

常规优化方式

Lint自动检测

// 扫描res资源文件
Analyze > Run Inspection by Name > Unused resources
// 扫描无用代码
Analyze > Inspect code

  不过需要注意这里扫描为静态扫描,部分资源可能存在动态调用,再删除的时候需要再三确认 ,Inspect code扫描出来的一样为静态扫描结果,反射和动态引用的代码是不会出现在这里的。

常规资源压缩

  这里主要以图片资源来进行压缩优化,列举常用方案前,简单说下各个图片文件及其特征
jpg: 一种有损的基于直接色的图片格式 属于光栅类型,所以可以表示2的24次方种颜色,非常适合色彩丰富图片、渐变色,所以相对的 jpg图片文件大小较大。

png: 也是属于光栅类型,无损压缩格式的基于8为索引色的位图格式称为png-8,支持透明度 并且文件尺寸相比jpg更小。还有一种png-24则是基于直接色的位图格式,图片存储相对较大,但是可以展示比较丰富的图像色彩。

gif: 光栅格式的图像文件类型。它使用无损压缩,但将图像“限制”为每像素 8 位和 256 色的有限调色板,常用于动画图像。

svg: 矢量图像文件类型,使用笛卡尔平面上的线和曲线系统,与总面积相比,而不是任何单个像素,可以无限放大而不会失真

Webp: 供更好的无损和有损图像压缩而开发的图像格式,相较于png 格式还可以压缩 10% -30%大小并且可以获得相同的质量

   主要有两种资源优化手段 :png文件压缩和替换为 Webp图片。

  1. png压缩,平时主要使用 https://tinypng.com/ ,不过很多UI平台都会在切图上传的时候进行自动压缩。
  2. 转为Webp, Android Studio可以右键资源文件 Convert to Webp 一键切换。

代码混淆

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

压缩( -dontshrink 关闭压缩):默认开启,用以减小应用体积,移除未被使用的类和成员,并且会在优化动作执行之后再次执行(因为优化后可能会再次暴露一些未被使用的类和成员)。

优化( -dontoptimize ):默认开启,在字节码级别执行优化,让应用运行的更快。

-dontoptimize  关闭优化
-optimizationpasses n 表示proguard对代码进行迭代优化的次数,Android一般为5

混淆( -dontobfuscate 关闭混淆):默认开启,增大反编译难度,类和类成员会被随机命名,除非用keep保护。

D8 R8优化

  Android Studio 3.1 或之后的版本 D8 将会被作为默认的 Dex 编译器,相较于D8,重点说下R8.

R8 官网地址

  根据官方说法,R8 是 Proguard 压缩与优化部分的替代品,并且它仍然使用与 Proguard 一样的 keep 规则。如果我们仅仅想在 Android Studio 中使用 R8,在 build.gradle 中打开混淆的时候,R8 就已经默认集成进 AGP 中了。

那么,R8 与混淆相比优势在哪里呢?

ProGuardR8 都应用了基本名称混淆:它们 都使用简短,无意义的名称重命名类,字段和方法。他们还可以 删除调试属性。但是,R8 在 inline 内联容器类中更有效,并且在删除未使用的类,字段和方法上则更具侵略性。例如,R8 本身集成在 ProGuard V6.1.1 版本中,在压缩 apk 的大小方面,与 ProGuard8.5% 相比,使用 R8 apk 尺寸减小了约 10%。并且,随着 Kotlin 现在成为 Android 的第一语言,R8 进行了 ProGuard 尚未提供的一些 Kotlin 的特定的优化。

从表面上看,ProGuardR8 非常相似。它们都使用相同的配置,因此在它们之间进行切换很容易。放大来看的话,它们之间也存在一些差异。R8 能更好地内联容器类,从而避免了对象分配。但是 ProGuard 也有其自身的优势,具体有如下几点:

  • ProGuard 在将枚举类型简化为原始整数方面会更加强大。它还传递常量方法参数,这通常对于使用应用程序的特定设置调用的通用库很有用。ProGuard 的多次优化遍历通常可以产生一系列优化。例如,第一遍可以传递一个常量方法参数,以便下一遍可以删除该参数并进一步传递该值。删除日志代码时,多次传递的效果尤其明显。ProGuard 在删除所有跟踪(包括组成日志消息的字符串操作)方面更有效
  • ProGuard 中应用的模式匹配算法可以识别和替换短指令序列,从而提高代码效率并为更多优化打开了机会。在优化遍历的顺序中,尤其是数学运算和字符串运算可从中受益
  • ProGuard 具有独特的能力来优化使用 GSON 库将对象序列化或反序列化为 JSON 的代码。该库严重依赖反射,这很方便,但效率低下。而 ProGuard 的优化功能可以 通过更高效,直接的访问方式 来代替它。

重复资源过滤

  由于大项目大多采用组件或插件化,多个模块之间可能存在资源的重复引入,常见的解决方法是通过资源包中的每个ZipEntry的CRC-32 checksum来筛选出重复的资源;通过android-chunk-utils修改resources.arsc,把这些重复的资源都重定向到同一个文件上; 把其它重复的资源文件从资源包中删除。

根据美团的方案之前实践过,并没有落地 实现方式较复杂,并且收益率不太高,本人并没有落地到项目中,有落地的同学可以反馈下实际优化提升。

ENUM减少使用

  如可以在开发过程 尽量减少 enum 的使用,每减少一个 enum 可以减少大约 1.0 到 1.4 KB 的大小

so文件移除

  市面上的手机cpu大多是arm架构的,所以保留arm的一种即可(定制的除外),armeabi-v7aarmeabi都可,其他直接删除。

复制代码
android {defaultConfig {ndk {abiFilters 'armeabi-v7a'}}
}

  在模拟器调试,就加上x86的架构,在local.properties中变量控制,正式包移除即可

  还有一种情况是不同的第三方库中存在相同的so文件
pickFirst只会打包第一个遇到的冲突的so,merge(碰到冲突会合并)和exclude(直接排除匹配到的文件,不建议使用)

packagingOptions {pickFirst 'lib/arm64-v8a/libgnustl_shared.so'pickFirst 'lib/armeabi-v7a/libgnustl_shared.so'
}

三方库处理

  实际项目开发中,各个模块都会有不同的移动团队开发,那么就可能存在重复引用的情况,比如某些不同的三方库,可能底层存在依赖同一套库的代码,那么在依赖的时候就可以进行去除

implementation('com.allenliu.versionchecklib:library:2.0.5') {exclude group: 'com.android.support', module: 'appcompat-v7'exclude group: 'com.android.support.constraint', module: 'constraint-layout'exclude group: 'org.greenrobot', module: 'eventbus'exclude group: 'com.squareup.okhttp3', module: 'okhttp'
}

三方库整合

  比如RN中使用的图片加载库是 Fresco ,但是Native使用的Glide ,那么我们就可以通过整合此类Case 达到缩减包大小目的。

  还有一种场景 比如一个三方库我们只使用一部分代码,完全可以拉出来魔改下,引入到自己的工具库中,减少多余代码引入。

进阶优化手段

插件化

   依赖于插件化的特点,将整个App拆分为多个模块,每个模块可单独运行 都是APK,最终打包的时候将宿主Apk和插件Apk分开打包,发布的时候只需要发布宿主Apk即可,用户进入不同场景按需动态下载对应Apk即可,可以很大程度上优化包体积问题。

重复技术方案筛选

  如果项目中由于历史包袱,存在多个跨端方案,比如存在 Web、小程序和 Flutter ,如果业务允许,可以将多余跨端方案移除,相对应的引擎So文件即可进行删除,起到减少包体积目的。

So动态化下载

  以Flutter为例,考虑到引擎加载和初始化时间,项目中首页一般还是采取Native方式展示,跨端场景大多在二级页面或非首页场景,按照按需思想,Flutter So也可以不放在本地,我们可以在进入跨端页面路由前,或进入App闲时阶段,添加动态下载So逻辑,相同思想,比如一些音视频文件非启动必须得话,也可以按需下载加载,本地尽量不放大文件。

DebugItem

  JVM 运行时加载的是 .class 文件,而 Android 为了使包大小更加紧凑、运行时更加高效就发明了 Dalvik 和 ART 虚拟机,两种虚拟机运行的都是 .dex 文件,当然 ART 虚拟机还可以同时运行 oat 文件。

  所以 Dex 文件里的信息内容和 Class 文件包含的信息是一样的,不同的是 Dex 文件对 Class 中的信息做了去重,一个 Dex 包含了很多的 Class 文件,并且在结构上有比较大的差异,Class 是流式的结构,Dex 是分区结构,Dex 内部的各个区块间通过 offset 来进行索引。

  为了在应用出现问题时,我们能在调试的时候去显示相应的调试信息或者上报 crash 或者主动获取调用堆栈的时候能通过 debugItem 来获取对应的行号,我们都会在混淆配置中加上下面的规则:

-keepattributes SourceFile, LineNumberTable

这样就会保留 Dex 中的 debug 与行号信息。根据 Google 官方的数据,debugItem 一般占 Dex 的比例有 5% 左右

ReDex

  ReDex 是 Facebook 开发的一个 Android 字节码的优化工具。它提供了 .dex 文件的读写和分析框架,并提供一组优化策略来提升字节码。官方提供预期优化效果:对dex文件优化为 8%

后期维稳

  在打包发布环节,可以编写插件针对各个模块大小或资源文件扫描,设置规则,写进流程中,可以防止包大小爆发式增长,当然好的包大小资源压缩思维,也可以帮助Apk维持在一个健康的水位上。

思考

   以上记录的种种方式,可根据项目状态进行选择优化,抓大放小,实事求是,也不能一味的以包大小越小越好,不影响业务,避免引起线上事故需要放在包大小之前衡量,避免得不偿失。

参考文章

美团 Android App包瘦身优化实践

jsonChao Android包体积优化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/652093.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Java SSM框架实现大学生综合素质评分平台项目【项目源码+论文说明】

基于java的SSM框架实现大学生综合素质评分平台演示 摘要 21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所…

C#调用SqlSugar操作达梦数据库报错“无效的表或视图名”

安装达梦数据库后,使用SqlSugar连接测试数据库并基于DBFirst方式创建数据库表对应的类,主要代码如下: SqlSugarClient db new SqlSugarClient(new ConnectionConfig(){DbType DbType.Dm,ConnectionString "Serverlocalhost; User Id…

qemu单步调试arm64 linux kernel

一、背景和目的 qemu搭建arm64 linux kernel环境-CSDN博客 之前介绍了qemu启动kernel的配置步骤和方法,现在开始我们的调试,这篇文章主要讲解如何单步调试内核,所有的实验还是基于ARM64; 二、环境准备 需要准备hostx86 target…

容器化部署 Jenkins,并配置SSH远程操作服务器

目录 一、Jenkins是什么 二、常见的部署Jenkins的方法 三、为什么选择容器化部署 四、容器化部署Jenkins步骤 1、安装 Docker 2、获取 Jenkins 镜像 3、创建并运行容器 4、访问 Jenkins 4.1 查看初始密码问题 5、配置 Jenkins 5.1 安装插件 5.2 创建管理员用户 5.3…

Java项目实战--瑞吉外卖DAY03

目录 P22新增员工_编写全局异常处理器 P23新增员工_完善全局异常处理器并测试 p24新增员工_小结 P27员工分页查询_代码开发1 P28员工分页查询_代码开发2 P22新增员工_编写全局异常处理器 在COMMON新增全局异常捕获的类,其实就是代理我们这些controlle。通过aop把…

基于SSM+Shiro+Druid实现的企业资产后台管理系统

系统介绍 系统演示 关注【全栈小白】视频号查看演示视频 随着企业的发展,很多中小企业的规模越来越大,需要管理资产也越来越多,比如显示器,主机,打印机,传真机,复印机,电话&#…

SAP-PP: BOM选择标识不存在

在MM03 中选择生产版本无法打开 原因是未维护 BOM选择标识 配置路径:生产→物料需求计划→工厂参数→执行工厂参数总体维护→BOM/工艺路线选择 T-CD: OPPQ

flutter极光推送配置厂商通道(华为)笔记--进行中

一、基础集成按照下面官方文档进行 厂商通道相关参数申请教程 集成厂商 集成指南 官方文档:厂商通道回执配置指南 注意:不同厂商对app上架的要求不同,华为、荣耀 对app上架没有硬性要求 遇到问题 1、引入apply plugin: com.huawei.agconn…

Jenkins部署及应用

一. 简介 二. 下载地址源: …

Docker部署Stable-Diffusion-webui

前排提示:如果不想折腾,可直接跳到最后获取封装好的容器,一键运行 :D 前言 乘上AI生成的快车,一同看看沿途的风景。 启一个miniconda容器 docker run -itd -v 宿主机内SD项目路径:/tmp --gpus all --ipc host -p 7860:7860 con…

机器学习---可能近似正确(PAC)、出错界限框架

1. 计算学习理论概述 从理论上刻画了若干类型的机器学习问题中的困难和若干类型的机器学习算法的能力 这个理论要回答的问题是: 在什么样的条件下成功的学习是可能的? 在什么条件下某个特定的学习算法可保证成功运行? 这里考虑两种框架&…

《SPSS统计学基础与实证研究应用精解》视频讲解:数据结构重组(数据重组方式的选择、由变量组到样本观测值组的重组、由样本观测值组到变量组的重组)

《SPSS统计学基础与实证研究应用精解》4.11 视频讲解 视频为《SPSS统计学基础与实证研究应用精解》张甜 杨维忠著 清华大学出版社 一书的随书赠送视频讲解4.11节内容。本书已正式出版上市,当当、京东、淘宝等平台热销中,搜索书名即可。本书旨在手把手教会…

2024年阿里云幻兽帕鲁Palworld游戏服务器优惠价格表

自建幻兽帕鲁服务器租用价格表,2024阿里云推出专属幻兽帕鲁Palworld游戏优惠服务器,配置分为4核16G和4核32G服务器,4核16G配置32.25元/1个月、10M带宽66.30元/1个月、4核32G配置113.24元/1个月,4核32G配置3个月339.72元。ECS云服务…

求x大于等于n的最小因子-codeforce round 921 div2 B

CF的round921div2的B题 题目简介与分析 有一堆废话我就不提了,直奔重点,然后是他问你x分成n个数相加的形式,然后要求这n个数的最大公因数最大,并问你最大时这个最大公因数是多少。 我的思路 我把x看成很多个质因数相乘得到&…

Linux cat,tac,more,head,tail命令 查看文本

目录 一. cat 和 tac命令二. head 和 tail 命令三. more命令 一. cat 和 tac命令 cat:用来打开文本文件,从上到下的顺序显示文件内容。tac:用法和cat相同,只不过是从下到上逆序的方式显示文件内容。当文件的内容有很多的时候&…

【Image captioning】论文阅读八—ClipCap: CLIP Prefix for Image Captioning_2021

中文标题:ClipCap: CLIP前缀用于图像描述(ClipCap: CLIP Prefix for Image Captioning) 文章目录 1. 介绍2. 相关工作3. 方法3.1 综述3.2 语言模型微调3.3 映射网络架构3.4 推理 4. 结果5. 结论 摘要:图像描述是视觉语言理解中的…

函数入门.

函数入门 1. 初识函数2. 函数的参数2.1 参数2.2 默认参数2.3 动态参数 3. 函数返回值总结作业 1. 初识函数 函数到底是个什么东西? 函数,可以当做是一大堆功能代码的集合。 def 函数名():函数内编写代码......函数名()例如: # 定义名字叫in…

【Axure高保真原型】可视化环形图

今天和大家可视化环形图的原型模板,,包括4种效果,移入变色在环形中部显示数据、移入变色在标签弹窗显示数据、移入放大在环形中部显示数据、移入放大在标签弹窗显示数据。这个原型是用Axure原生元件制作的,所以不需要联网或者调用…

项目中从需求分析到研发上线

一、背景 应用系统从设想到需求到研发到上线会经历一些列工程化过程。比如经典的瀑布模型工作流,其实就是一个经过很多经验总结下来的工程方法。本节阐述项目中从需求到研发上线的过程。但是也有些根据不同的行业,不同的公司,不同管理者的风…

Spring Boot使用AOP

一、为什么需要面向切面编程? 面向对象编程(OOP)的好处是显而易见的,缺点也同样明显。当需要为多个不具有继承关系的对象添加一个公共的方法的时候,例如日志记录、性能监控等,如果采用面向对象编程的方法&…