谷歌新安装包文件形式 .aab 在UE4中的打包原理
摘要
本文学习了aab的基本概念以及UE4中产生aab的构建原理。
从官网了解基本概念
官网:Android Developers
1、什么是aab?
.aab包形如:
2021年7月,在Google Play应用程序中,已经有数千个应用程序率先跟进了AAB格式。谷歌宣布从2021年8月份开始,所有提交到Google Play商店的新应用必须采用AAB格式。
全称Android App Bundles。谷歌在2018年启用了AAB新格式(AAB全称为Android App Bundles”),谷歌声称这种新格式将使应用程序文件更小,意味着aab分布式应用程序比通用apk平均少占用15% 的空间。更重要的是,它拓展了应用程序捆缚包的定义,只包含运行App时的必要代码。也就是说,下载了一部分之后,App就可以直接运行,无需等待下载完成再安装。
2、什么是PAD?
全称 Play Asset Delivery,它是能够打包出 aab 的组件。
3、三个分发模式是什么?
- install-time 资源包在用户安装应用时分发。这些资源包以拆分 APK(APK 集的一部分)的形式提供。它们也称为“预先”资源包;您可以在应用启动时立即使用这些资源包。这些资源包会增加 Google Play 商店上列出的应用大小。用户无法修改或删除这些资源包。
- fast-follow 资源包会在用户安装应用后立即自动下载;用户无需打开应用即可开始 fast-follow 下载。下载过程中,用户仍然可以进入应用。这些资源包会增加 Google Play 商店上列出的应用大小。
- on-demand 资源包会在应用运行时下载。
Google Play 商店会以归档文件(而非拆分 APK)的形式提供配置为 fast-follow 和 on-demand 的资源包。然后,这些资源包会在应用的内部存储空间中展开。
4、资源更新的方式
更新应用时,install-time Asset Pack 会作为基础应用更新的一部分进行更新(开发者无需执行任何操作)。
对于 fast-follow 和 on-demand Asset Pack 的应用更新,则遵循以下步骤:
1 系统将应用的补丁程序(包括所有资产)下载到设备上的安全位置。
2 更新应用二进制文件;这包括所有 install-time Asset Pack。
3 之前下载的所有 Asset Pack 变为无效。
4 将资源的补丁复制并应用到存储在应用内部存储空间中的资源。
5、什么是apks?apks,即apk split,分散的apk。
现在我们从谷歌商店上下载的应用多半都是apks格式的。
apks格式的安装包,就是使用了Android App Bundle(AAB)技术生成的安装包。
这种技术实际上就是将apk文件拆分成多个小包,再根据当前的设备选择适当的文件下载安装。
通过这种技术,谷歌商店就可以根据我们的设备生成一个适合当前设备的安装包,例如使用大屏幕设备的用户就不需要下载小屏幕的资源,使用arm v8架构设备的用户就不需要下载arm v7架构的资源(当然还存在x86架构的),使用中文的用户就不需要下载其他的语言文件,这样就可以在实现更多功能的同时减小app占用空间的大小。并且为开发者提供了灵活的分发方式和极高的性能。同时apks将不再支持使用obb数据包拓展软件,这也意味着使用obb数据包的xapk已经成为过去式了。
6、互斥性。
打apk就不应该打aab、apks;打aab、apks就不应该打apk。aab、apks应该同时出现。
开关
在设置中找到开关 “Generate bundle (AAB)”,打开它表示生成Google的AAB。
位置是:
EngineSource\Engine\Source\Runtime\Android\AndroidRuntimeSettings\Classes\AndroidRuntimeSettings.h
// Enables generating AAB bundleUPROPERTY(GlobalConfig, EditAnywhere, Category = "App Bundles", Meta = (DisplayName = "Generate bundle (AAB)"))bool bEnableBundle;
它影响了 EngineSource\Engine\Source\Programs\UnrealBuildTool\Platform\Android\UEDeployAndroid.cs 中的逻辑,大致是:
// 位于 private void MakeApk(...) 方法中if (!bEnableBundle) // 如果打的是 apk
{ …… RunCommandLineProgramWithExceptionAndFiltering(UE4BuildGradlePath, ShellExecutable, ShellParametersBegin + "\"" + GradleScriptPath + "\" " + GradleOptions + ShellParametersEnd, "Making .apk with Gradle...");……
}
else // 如果打的是aab、apks
{ ……RunCommandLineProgramWithExceptionAndFiltering(UE4BuildGradlePath, ShellExecutable, ShellParametersBegin + "\"" + GradleScriptPath + "\" " + GradleOptions + ShellParametersEnd, "Making .aab with Gradle...");……
}
所谓RunCommandLineProgramWithExceptionAndFiltering,字面上执行的是:
// 【书签1】
2024-10-12 15:14:19:037 : VERBOSE:
2024-10-12 15:14:19:037 : Running: cmd.exe /c "{我的项目}\Intermediate\Android\gradle\rungradle.bat" --stacktrace :app:bundleDebug --init-script init.gradle --profile
从字面意思 rungradle.bat 可以得知,无论是打 .apk 还是打 .aab/.apks,都是用 Gradle 来打包的。Gradle是安卓的构建打包管理工具。关于Gradle,推荐阅读《实战Gradle(中文完整版)》这本书。
rungradle.bat
这个脚本位于 {我的项目}\Intermediate\Android\gradle\.gradle 目录,它是 Engine\Source\Programs\UnrealBuildTool\Platform\Android\UEDeployAndroid.cs 生成出来的。内容如下:
@echo off
setlocal
set GRADLEPATH=%~dp0
set GRADLE_CMD_LINE_ARGS=
:setupArgs
if ""%1""=="""" goto doneStart
set GRADLE_CMD_LINE_ARGS=%GRADLE_CMD_LINE_ARGS% %1
shift
goto setupArgs:doneStart
subst Z: "%CD%"
pushd Z:
call "%GRADLEPATH%\gradlew.bat" %GRADLE_CMD_LINE_ARGS%
set GRADLEERROR=%ERRORLEVEL%
popd
subst Z: /d
exit /b %GRADLEERROR%
它调用的是gradlew.bat,在日志中会衔接Gradle的日志,如下:
Package
MakeApk函数包括了“生成apk”与“生成.aab、.apks”的功能,MakeApk函数发生在 UE4的package阶段。具体的调用过程示意图:
【书签1】的日志,就是在这个过程中间来的,见 UEDeployAndroid.cs 中。尽管叫做 UEDeployAndroid.cs ,但Package阶段会调用它。
众所周知,Package步骤对应的脚本是:
%EnginePath%\Engine\Build\BatchFiles\RunUAT.bat BuildCookRun -project=%ClientPath%\%ProjectName%.uproject -noP4 -platform=Android -client -clientconfig=%TargetBuildConfig% -cookflavor=%COOK_FLAVOR% %DISTRIBUTION% -skipcook -pak -compressed -stage -NoDebugInfo -package -ignorejunk -nocompile -archive -archivedirectory=%ClientPath%\Saved\Archived -manifests -Verbose
这里参数很多,不是这里的重点。这里说一个技巧,如何让【书签1】所对应的c#脚本,能够输出verbose日志呢?做法是为 RunUAT.bat 脚本传参 -Verbose,如上面命令的末尾处。
而Gradle的日志可以在哪里看呢?
打AAB包时,将会在这个位置 C:\android_build_tools\gradle-4.1-rc-2\daemon\7.2 (gradle所在环境)产生日志文件。
UE4中的Gradle工程
UE4的Gradle工程位于 {我的项目}\Intermediate\Android\gradle ,结构如下:
我在查一些无聊的问题(下面两个问题)时,发现清除UE4的Intermediate、Binaries目录,能够有效果,推测是Gradle工程脏了导致的,因此,删除Gradle工程是一个好的选择,毕竟它在Intermediate中,可以安全地删除,删除后UE4会重新产生Gradle工程。
(问题1:“main.obb.png”出现在.aab的base目录中,而不是在obbassets目录中)
(问题2:.apk、.aab|.apks 都会错误地同时出,而应该只出apk或只出aab|apks)
GooglePAD_APL.xml
在EngineSource下,GooglePAD 是谷歌生成.aab的模块。GooglePAD_APL.xml 是其中一个中间作用文件,它搭建了 UE4 到 Gradle 的桥梁,具体的关系如下:
上图只是简单阐述,下面将会仔细理解GooglePAD_APL.xml。接下来,我描述的方式是:贴一段文件内容,然后接一些解释。注意,并非所有的内容都贴出来了。
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:android="http://schemas.android.com/apk/res/android">
<init>
<log text="GooglePAD Plugin Init"/>
<setBoolFromProperty result="bEnabled" ini="Engine" section="/Script/GooglePADEditor.GooglePADRuntimeSettings" property="bEnablePlugin" default="false"/>
<setBoolFromProperty result="bOnlyDistribution" ini="Engine" section="/Script/GooglePADEditor.GooglePADRuntimeSettings" property="bOnlyDistribution" default="true"/>
依据 /Script/GooglePADEditor.GooglePADRuntimeSettings 的UE4配置,来设置变量值。
<!-- NDK path -->
<setString result="NDKVersion" value="ndk21.4.7075529"/>
声明一个字符串变量。
<if condition="bEnabled">
<true>
<!-- disable if app bundle disabled -->
<if condition="bEnableBundle">
<false>
<log text="Disabled because not generating AAB bundle"/>
<setBool result="bEnabled" value="false"/>
</false>
</if>
等价于
if bEnabled: if not bEnableBundle: bEnabled = False
当if else语句多起来时,这种描述方式会让人抓狂。
<resourceCopies>
<log text="Copying libplaycore.so and proguard files"/>
<!-- note: have to stage this since we linked it -->
<copyFile src="$S(AbsPluginDir)/../ThirdParty/play-core-native-sdk/libs/$S(Architecture)/$S(NDKVersion)/c++_shared/libplaycore.so"
dst="$S(BuildDir)/libs/$S(Architecture)/libplaycore.so" />
<isDistribution>
<copyDir src="$S(AbsPluginDir)/../ThirdParty/play-core-native-sdk/proguard"
dst="$S(BuildDir)/gradle/app/proguard" />
</isDistribution>
</resourceCopies>
一段和资源拷贝有关的逻辑。
<settingsGradleAdditions>
<if condition="bEnabled">
<true>
<insert>
<![CDATA[
// generate mainobb assetpack
if (OBB_FILECOUNT.toInteger() > 0) {
File obbfile = new File(OBB_FILE0)……
]]>
<![CDATA[ 后面的内容,直到 ]]> ,是XML语言中的表示纯粹字符串。这段表示对 settings.gradle 追加字符串,稍后会介绍 settings.gradle。
<gameActivityOverrideAPKOBBPackaging>
<if condition="bEnabled">
<true>
<insert>
<
谷歌新安装包文件形式 .aab 在UE4中的打包原理
摘要 本文学习了aab的基本概念以及UE4中产生aab的构建原理。 从官网了解基本概念 官网:Android Developers 1、什么是aab? .aab包形如: 2021年7月,在Google Play应用程序中,已经有数千个应用程序率先跟进了AAB格式。…...

昂首平台:多货币专家顾问助力投资者优化外汇投资
昂首平台推出的多货币专家顾问(EA)为投资者提供了多样化的货币对交易选择。考虑到外汇市场的复杂性,大多数EA系统专注于价差较低的主要货币对,如EUR/USD或GBP/USD。交易那些价差较大的非主流货币对,如EUR/JPY和AUD/CAD,可能会增加…...
Go标准库runtime.MemStats
在 Go 语言中,runtime.MemStats 是一个结构体,它提供了关于 Go 程序内存使用情况的统计信息。这个结构体是 runtime 包中的 MemStats 类型,它包含了多个字段,用于报告内存分配器的统计数据,如内存分配、释放、垃圾回收…...

MAC 电脑Office power point编辑的时候,显示“某些字体无法随演示文稿一起保存,仍然要保存演示文稿吗?”
目录 问题描述: 问题解决: 问题描述: 在使用mac电脑的power point打开别人的.ppt文件之后,在保存的时候,弹出““某些字体无法随演示文稿一起保存,仍然要保存演示文稿吗?”,每次只…...

R语言机器学习算法实战系列(四)随机森林算法+SHAP值 (Random Forest)
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍教程下载数据加载R包导入数据数据预处理数据描述特征选择数据切割调节参数构建模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve特征的重要性模型解释保…...

用柔性神经k-Opt学习搜索路径问题的可行和不可行区域(未完,先看前驱文章L2S)
文章目录 Abstract1 IntroductionAbstract 介绍了一种名为 Neural k-Opt(NeuOpt)的新型学习搜索(L2S)求解器,用于解决路径问题。它学习执行基于定制的动作分解方法和定制的循环双流(Recurrent Dual-Stream)解码器的灵活 k-opt 交换。 作为一项开创性的工作,我们绕过了…...

【升华】人工智能python重要库scikit-learn学习
一、人工智能python重要库scikit-learn 在人工智能10大算法中,有8个算法都导入了 sklearn库 from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn import metrics # 导入所需的库 from sklea…...

Stable Diffusion Web UI 大白话术语解释 (二)
归纳整理,Stable Diffusion Web UI 使用过程中,相关术语 ControlNet ControlNet 说简单点,就是你可以给 AI 一些“规则”,比如让它根据某些线条、结构或者骨架去画图。 这样能让 AI 画出更符合你要求的图片,特别适合画…...
vue-vben-admin 首页加载慢优化 升级vite2到vite3
我的vben-admin是2.8版本的,首次首页加载太慢了,升级下vite,原来1分钟,现在20s左右 1.修改package.json 添加 "terser": "^5.14.2",修改 "vitejs/plugin-legacy": "^2.0.0","vitejs/plugin-vue": "^3.0.1",&qu…...

集合框架07:LinkedList使用
1.视频链接:13.14 LinkedList使用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1zD4y1Q7Fw?spm_id_from333.788.videopod.episodes&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5&p142.LinkedList集合的增删改查操作 package com.yundait.Demo01;im…...

一区鱼鹰优化算法+深度学习+注意力机制!OOA-TCN-LSTM-Attention多变量时间序列预测
一区鱼鹰优化算法深度学习注意力机制!OOA-TCN-LSTM-Attention多变量时间序列预测 目录 一区鱼鹰优化算法深度学习注意力机制!OOA-TCN-LSTM-Attention多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.基于OOA-TCN-LSTM-Attenti…...

Cesium 黑夜效果
Cesium 黑夜效果 原理: 根据相机到片元的距离雾化场景的后处理效果 效果:...
leetcode动态规划(二)-斐波那契数列
题目 509.斐波那契数列 斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) 0,F(1) 1 F(n) F(n - 1) F(n - 2)࿰…...

【MySQL】增删改查-进阶(一)
目录 🌴数据库约束 🚩约束类型 🚩NOT NULL 🚩UNIQUE 🚩DEFAULT 🚩PRIMARY KEY 🚩FOREIGN KEY 🚩CHECK 🎄表的设计 🚩一对一 🚩一对多 …...

MacOS RocketMQ安装
MacOS RocketMQ安装 文章目录 MacOS RocketMQ安装一、下载二、安装修改JVM参数启动关闭测试关闭测试测试收发消息运行自带的生产者测试类运行自带的消费者测试类参考博客:https://blog.csdn.net/zhiyikeji/article/details/140911649 一、下载 打开官网,…...

OpenCV高级图形用户界面(6)获取指定窗口中图像的矩形区域函数getWindowImageRect()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 提供窗口中图像的矩形区域。 该函数 getWindowImageRect 返回图像渲染区域的客户端屏幕坐标、宽度和高度。 函数原型 Rect cv::getWindowImage…...

SpringColoud GateWay 核心组件
优质博文:IT-BLOG-CN 【1】Route路由: Gateway的基本构建模块,它由ID、目标URL、断言集合和过滤器集合组成。如果聚合断言结果为真,则匹配到该路由。 Route路由-动态路由实现原理: 配置变化Apollo 服务地址实例变化…...

5.计算机网络_抓包工具wireshark
安装 Linux中安装wireshark: sudo apt-get install wireshark Linux中执行wireshark: sudo wireshark 使用 注意:只有与外网交互的数据才可以被wireshark抓到,本机回环的数据不会被抓到 实验内容: 使用nc命令…...

基于Java的车辆管理系统的设计与实现-计算机毕业设计源码41727
摘要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题。针对车辆管理系统等问题,对车辆管理…...
在软件开发中低耦合和高内聚是什么,如何实现,请看文章
软件开发中,“低耦合”和“高内聚”是设计原则,用于提高系统的可维护性、可扩展性和可重用性。下面我会详细解释这两个概念及其带来的好处和规避的坏处。 低耦合(Low Coupling) 定义: 低耦合指的是模块之间的依赖关系…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...