vivo 游戏中心包体积优化方案与实践

作者:来自 vivo 互联网大前端团队- Ke Jie

介绍 App 包体积优化的必要性,游戏中心 App 在实际优化过程中的有效措施,包括一些优化建议以及优化思路。

一、包体积优化的必要性

安装包大小与下载转化率的关系大致是成反比的,即安装包越大,下载转换率就越差。Google 曾在 2019 的谷歌大会上给出过一个统计结论,包体积体大小每上升 6MB,应用下载转化率就会下降 1%,在不同地区的表现可能会有所差异。

APK 减少 10MB,在不同国家转化率增长

(注:数据来自于 googleplaydev:Shrinking APKs, growing installs)

二、游戏中心 APK 组成

APK 包含以下目录:

  • META-INF/:包含 CERT.SF 、CERT.RSA 签名文件、MANIFEST.MF 清单文件。

  • assets/:包含应用的资源。

  • res/:包含未编译到 resources.arsc 中的资源。

  • lib/:支持对应 CPU 架构的 so 文件。

  • resources.arsc:资源索引文件。

  • classes.dex:可以理解的 dex 文件就是项目代码编译为 class 文件后的集合。

  • AndroidManifest.xml:包含核心 Android 清单文件。此文件列出了应用的名称、版本、访问权限和引用的库文件。

发现占包体积比较大的主要是 lib、res、assets、resources 这几个部分,优化主要也从这几个方面入手。

三、包体积检测工具

Matrix-ApkChecker 作为 Matrix 系统的一部分,是针对 Android 安装包的分析检测工具,根据一系列设定好的规则检测 APK 是否存在特定的问题,并输出较为详细的检测结果报告,用于分析排查问题以及版本追踪。

配置游戏中心的 Json,主要检测 APK 是否经过了资源混淆、不含 Alpha 通道的 PNG 文件、未经压缩的文件类型、冗余的文件、无用资源等信息。

对于生成的检测文件进行分析,可以优化不少体积。

工具 Matrix Apkcheck 介绍:https://github.com/Tencent/matrix/wiki/Matrix-Android-ApkChecker

四、包体积优化措施

4.1 不含 Alpha 通道的 PNG 大图

项目中存在较多这种类型的图,可以替换为 JPG 或者 WebP 图,能减少不少体积。

4.2 代码做减法

随着业务的迭代,很多业务场景是不会再使用了,涉及到相关的资源和类文件都可以删除掉,相应的 APK 中 res 和 dex 都会相应减少。游戏中心这次去掉了些经过迭代后没有使用的业务场景和资源。

4.3 资源文件最少化配置

针对内销的项目,本地的 string.xml 或者 SDK 中的 string.xml 文件中的多语言,是根本用不到的。这部分资源可以优化掉,能减少不少体积。

在 APP 的 build.gradle 中下添加 resConfigs "zh-rCN", "zh-rTW", "zh-rHK"。这样配置不影响英文、中文、中国台湾繁体、中国香港繁体语言的展示。

资源文件最少化配置前

资源文件最少化配置后

4.4 配置资源优化

很多项目为了适配各种尺寸的分辨率,同一份资源可能在不同的分辨率的目录下放置了各种文件,然后现在主流的机型都是 xxh 分辨率,游戏游戏中心针对了内置的 APK,配置了优先使用"xxhdpi", "night-xxhdpi"。

这么配置如果 xxhdpi、night-xxhdpi 存在资源文件,就会优先使用该分辨率目录下文件,如果不存在则会取原来分辨率目录下子资源,能避免出现资源找不到的情形。

defaultConfig {resConfigs isNotBaselineApk ? "" : ["xxhdpi", "night-xxhdpi"]
}

4.5 内置包去除 v1 签名

同样对于内置包来说,肯定都是 Android 7 及以上的机型了,可以考虑去掉 v1 签名。

signingConfigs {gameConfig {if (isNotBaselineApk) {print("v1SigningEnabled true")v1SigningEnabled true} else {print("v1SigningEnabled false")v1SigningEnabled false}v2SigningEnabled true}
}

去掉 v1 签名后,上图的三个文件在 APK 中会消失,也能较少 600k 左右的体积。

4.6 动效资源文件优化

发现项目中用了不少的 GIF、Lottie 文件、SVG 文件,占用了很大一部分体积。考虑将这部分替换成更小的动画文件,目前游戏中心接入了 PAG 方案。替换了部分 GIF 图和 Lottie 文件。

PAG 文件采用可扩展的二进制文件格式,可单文件集成图片音频等资源,导出相同的 AE 动效内容,在文件解码速度和压缩率上均大幅领先于同类型方案,大约为 Lottie 的 0.5 倍,SVG 的 0.2 倍。

实际上可能由于设计导出的 Lottie 或者 GIF 不规范,在导出 PAG 文件时会提醒优化点,实际部分资源的压缩比率达到了 80~90%,部分动效资源从几百 K 降到了几十 K。

具体可以参考 PAG 官网:https://github.com/Tencent/libpag/blob/main/README.zh_CN.md

游戏中心这边将比较大的 GIF 图,较多的 Lottie 图做过 PAG 替换。

举例

(1)游戏中心的榜单排行页上的头图,UI 那边导出的符合效果的 GIF 大小为 701K,替换为 PAG 格式后同样效果的图大小为 67K,只有原来的 1/10 不到。

(2)游戏中心的入口空间 Lottie 动效优化。

一份 Lottie 动效大概是这样的,一堆资源问题加上 Json 文件。像上述动效的整体资源为 112K,同样的动效格式转换为 PAG 格式后,资源大小变成 6K,只有原大小的 5%左右。之后新的动效会优先考虑使用 PAG。

4.7 编译期间优化图片

以游戏中心 App 为例,图片资源约占用了 25%的包体积,因此对图片压缩是能立杆见效的方式。

WebP 格式相比传统的 PNG 、JPG 等图片压缩率更高,并且同时支持有损、无损、和透明度。

思路就是在是在 mergeRes 和 processRes 任务之间插入 WebP 压缩任务,利用 Cwebp 对图片在编译期间压缩。

(注:图片来源于https://booster.johnsonlee.io/zh/guide/shrinking/png-compression.html#pngquant-provider )

已有的解决方法

(1)可以采用滴滴的方案 booster,booster-task-compression-cwebp 。

参考链接:https://github.com/didi/booster

(2)公司内部官网模块也有类似基于 booster 的插件,基于 booster 提供的 API 实现的图片压缩插件。压缩过后需要对所有页面进行一次点检,防止图片失真,针对失真的图片,可以采用白名单的机制。

4.8 动态化加载 so

同样以游戏中心为例,so 的占比达到了 45.1%,可以对使用场景较少和较大的 so 进行动态化加载的策略,在需要使用的场景下载到本地,动态去加载。

使用的场景去服务端下载到本地加载的流程可以由以下流程图表示。

流程可以归纳为下载、解压、加载,主要问题就是解决 so 加载问题。

载入 so 库的传统做法是使用:

System.loadLibrary(library);

经常会出现 UnsatisfiedLinkError,Relinker 库能大幅减小报错的概率:

ReLinker.loadLibrary(context, "mylibrary")

具体可以参考:https://github.com/KeepSafe/ReLinker

按需加载的情形,风险与收益是并存的,有很多情况需要考虑到,比如下载触发场景、网络环境、加载失败是否有降级策略等等,也需要做好给用户的提示交互。

4.9 内置包只放 64 位 so

目前新上市的手机 CPU 架构都是 arm64-v8a, 对应着 ARMV8 架构,所以在打包的时候针对内置项目,只打包 64 位 so 进去。

ndk {if ("64" == localMultilib)abiFilters "arm64-v8a"else if ("32" == localMultilib)abiFilters "armeabi"elseabiFilters "armeabi", "arm64-v8a"}
//其中localMultilib为配置项变量String localMultilib = getLocalMultilib()
String getLocalMultilib() {def propertyKey = "LOCAL_MULTILIB"def propertyValue = rootProject.hasProperty(propertyKey) ? rootProject.getProperty(propertyKey) : "both"println " --> ${project.name}: $propertyKey[$propertyValue], $propertyKey[${propertyValue.class}]"return propertyValue
}

4.10 开启代码混淆、移除无用资源、ProGuard 混淆代码

android {buildTypes {release {minifyEnabled trueshrinkResources true}}
}

shrinkResources 和 minifyEnabled 必须同时开启才有效。

特别注意:这里需要强调一点的是开启之后无用的资源或者图片并没有真正的移除掉,而是用了一个同名的占位符号。

可以通过 ProGuard 来实现的,ProGuard 会检测和移除代码中未使用的类、字段、方法和属性,除此外还可以优化字节码,移除未使用的代码指令,以及用短名称混淆类、字段和方法。

proguard-android.txt 是 Android 提供的默认混淆配置文件,在配置的 Android sdk /tools/proguard 目录下,proguard-rules.pro 是我们自定义的混淆配置文件,我们可以将我们自定义的混淆规则放在里面。

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

4.11 R 文件内联优化

如果我们的 App 架构如下:

编译打包时每个模块生成的 R 文件如下:

R_lib1 = R_lib1;
R_lib2 = R_lib2;
R_lib3 = R_lib3;
R_biz1 = R_lib1 + R_lib2 + R_lib3 + R_biz1(biz1本身的R)
R_biz2 = R_lib2 + R_lib3 + R_biz2(biz2本身的R)
R_app = R_lib1 + R_lib2 + R_lib3 + R_biz1 + R_biz2 + R_app(app本身R)

可以看出各个模块的 R 文件都会包含下层组件的 R 文件内容,下层的模块生成的 id 除了自己会生成一个 R 文件外,同时也会在全局的 R 文件生成一个,R 文件的数量同样会膨胀上升。多模块情况下,会导致 APK 中的 R 文件将急剧的膨胀,对包体积的影响很大。

由于 App 模块目前的 R 文件中的资源 ID 全部是 final 的, Java 编译器在编译时会将 final 常量进行 inline 内联操作,将变量替换为常量值,这样项目中就不存在对于 App 模块 R 文件的引用了,这样在代码缩减阶段,App 模块 R 文件就会被移除,从而达到包体积优化的目的。

基于以上原理,如果我们将 library 模块中的资源 ID 也转化为常量的话,那么 library 模块的 R 文件也可以移除了,这样就可以有效地减少我们的包体积。

现在有不少开源的 R 文件内联方法,比如滴滴开源的 booster 与字节开源的 bytex 都包含了 R 文件内联的插件。

booster 参考:

https://booster.johnsonlee.io/zh/guide/shrinking/res-index-inlining.html#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8

bytex 参考:

https://github.com/bytedance/ByteX/blob/master/access-inline-plugin/README-zh.md

五、优化效果

5.1 优化效果

上述优化措施均在游戏中心实际中采用,以游戏中心某个相同的版本为例子,前后体积对比如下图所示:

(1)包体积优化的比例达到了 31%,包体积下降了 20M 左右,从长久来说对应用的转换率可以提升 3%的点左右。

(2)启动速度相对于未优化版本提升 2.2%个点。

5.2 总结

(1)读者想进行体积优化之前,需先分析下 APK 的各个模块占比,主要针对占比高的部分进行优化,比如:游戏中心中 lib、res、assets、resources 占比较高,就针对性的进行了优化;

(2)动效方案的切换、so 动态加载、编译期间图片优化等措施是长久的,相比于未进行优化,时间越长可能减少的体积越明显;

(3)资源文件最小化配置、配置资源优化,简单且效果显著;

(4)后续会对 dex 进行进一步探索,目前项目中代码基本上都在做加法,越来越复杂,很少有做减法,导致 dex 逐渐增大,目前还在探索怎么进一步缩小 dex 体积。

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

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

相关文章

数据库SQL——连接表达式(JOIN)图解

目录 一、基本概念 二、常见类型 内连接(INNER JOIN): 左连接(LEFT JOIN 或 LEFT OUTER JOIN): 右连接(RIGHT JOIN 或 RIGHT OUTER JOIN): 全连接(FULL…

Sigrity SPEED2000 Power Ground Noise Simulation模式如何查看PDS系统的自阻抗操作指导

Sigrity SPEED2000 Power Ground Noise Simulation模式如何查看PDS系统的自阻抗操作指导 Sigrity Power SI Power Ground Noise Simulation模式可以用于PDS系统自阻抗分析,以下图为例 2D视图

uni-app移动端与PC端兼容预览PDF文件

过程遇到的问题 1、如果用的是最新的版本的pdfjs的话,就会报Promise.withResolvers 不是一个方法的错误,原因是Promise.withResolvers是ES15新特性,想了解可参考链接,这里的解决方案是将插件里的涉及到Promise.withResolvers的地…

「Py」Python基础篇 之 Python都可以做哪些自动化?

✨博客主页何曾参静谧的博客📌文章专栏「Py」Python程序设计📚全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C」C/C程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定…

[ 网络安全介绍 5 ] 为什么要学习网络安全?

🍬 博主介绍 👨‍🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…

在Java中使用ModelMapper简化Shapefile属性转JavaBean实战

目录 前言 一、原始的处理办法 1、使用Set方法来转换 2、使用构造方法转换 二、基于ModelMapper的动态转换 1、ModelMapper简介 2、集成到项目中 3、Shapefile属性读取 三、总结 前言 在现代软件开发中,尤其是在多层架构中,经常需要将数据从一个…

时间管理的三个痛点

时间管理方面,有三个痛点:不知道、不平衡、不安全。 很多人,忙了一天,感觉很累,但是不知道做了什么。他不知道,这一天工作了几个小时,做了哪些事,分别用了多少时间,只是…

封装el-menu

案例图 数据格式 commonMenu.vue <template><div class"commonMenuStyle"><el-sub-menu v-if"hasChildren" :index"item.MenuId"><template #title><el-icon><location /></el-icon><!-- isColl…

微服务day07

Elasticsearch 需要安装elasticsearch和Kibana&#xff0c;应为Kibana中有一套控制台可以方便的进行操作。 安装elasticsearch 使用docker命令安装&#xff1a; docker run -d \ --name es \-e "ES_JAVA_OPTS-Xms512m -Xmx512m" \ //设置他的运行内存空间&#x…

假期增设:福祉与负担并存,寻求生活经济平衡之道

近年来&#xff0c;关于春节与五一假期各自增设一日的议题持续引发广泛热议。这一额外假期的增设&#xff0c;究竟是民众福祉的增益&#xff0c;还是社会运行的额外负担&#xff0c;值得我们深入探讨。 从宏观经济视角审视&#xff0c;假期的延长产生了复杂而深远的影响。一方面…

结构体(c语言)

一.结构体 1.结构的基础知识 结构是一些值得集合&#xff0c;这些值称为成员变量&#xff0c;结构得每个成员可以是不同类型的变量 2.结构体的声明 struct tag//结构体名称{member-list;//成员变量}variable-list;//全局变量 例&#xff1a;描述一个学生 struct Stu {int a…

GIS空间分析案例---城市公共设施配置与服务评价

今天给大家带来新的GIS案例分析——“城市公共设施配置与服务评价” 数据准备 本案例提供的数据资料如下&#xff1a; 武汉城区.shp&#xff1a;武汉市三环城区范围面要素&#xff1b; 武汉城区&#xff08;去除水系&#xff09;.shp&#xff1a;去除水系后的研究区范围面要…

如何在算家云搭建Peach-9B-8k-Roleplay(文本生成)

一、Peach-9B-8k-Roleplay简介 Peach-9B-8k-Roleplay 是一种聊天大型语言模型&#xff0c;它是通过我们的数据合成方法创建的超过 100K 的对话中微调 01-ai/Yi-1.5-9B 模型而获得的。 也许是 34B 以下参数最好的 LLM。 二、模型搭建流程 1. 创建容器镜像 进入算家云平台的“…

软件工程概论项目(二),node.js的配置,npm的使用与vue的安装

上一章我们配置了git仓库&#xff0c;这一章我们来配置项目需要用的一些其他的环境。 放一个思维导图在这里&#xff0c;可以参考一下&#xff0c;很不全面&#xff0c;没有参考价值,反正我先这样写吧。 参考了这个nodejs的配置&#xff0c;写的很好&#xff1a;https://blog.c…

Android中桌面小部件的开发流程及常见问题和解决方案

在Android中&#xff0c;桌面小部件&#xff08;App Widget&#xff09;是应用程序可以在主屏幕或其他地方显示的一个可视化组件&#xff0c;提供简化信息和交互功能。Android桌面小部件的framework为开发者提供了接口&#xff0c;使得可以创建和更新小部件的内容。以下是Andro…

JAVA题目笔记(十五)经典算法题

一、按要求排序 要求&#xff1a;定义数组并存储一些女朋友对象&#xff0c;利用Arrays中的sort方法进行排序 属性包括&#xff1a;姓名&#xff0c;年龄&#xff0c;身高 按照年龄大小进行排序&#xff0c;年龄一样按照身高排序&#xff0c;身高一样按照姓名字母进行排序。…

【JVM】关于JVM的内部原理你到底了解多少(八股文面经知识点)

前言 &#x1f31f;&#x1f31f;本期讲解关于HTTPS的重要的加密原理~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不…

【Pikachu】目录遍历实战

既然已经决定做一件事&#xff0c;那么除了当初决定做这件事的我之外&#xff0c;没人可以叫我傻瓜。 1.目录遍历漏洞概述 目录遍历漏洞概述 在Web功能的设计过程中&#xff0c;开发者经常会将需要访问的文件作为变量进行定义&#xff0c;以实现前端功能的灵活性。当用户发起…

如何用C#和Aspose.PDF实现PDF转Word工具

在本篇博文中&#xff0c;我将详细讲解如何用C#实现一个PDF转Word工具。这款工具基于Aspose.PDF库&#xff0c;实现PDF文件转为Word&#xff08;DOC/DOCX&#xff09;格式的功能&#xff0c;并通过用户友好的界面和状态提示提升用户体验。希望通过这篇文章帮助大家理解软件的实…

【图像压缩感知】论文阅读:Self-supervised Scalable Deep Compressed Sensing

tips&#xff1a;本文为个人阅读论文的笔记&#xff0c;仅作为学习记录所用。 Title&#xff1a;Self-supervised Scalable Deep Compressed Sensing Journal&#xff1a;IJCV 2024 代码链接&#xff1a;GitHub - Guaishou74851/SCNet: Self-Supervised Scalable Deep Comp…