谷歌新安装包文件形式 .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>
<![CDATA[
// for GooglePAD (use upfront for main.obb.png)
assetPackManager = AssetPackManagerFactory.getInstance(this);
表示追加字符串到 gradle\app\src\main\java\com\epicgames\ue4\GameActivity.java 中。GameActivity.java是运行时的逻辑。也许你会好奇,我如何得知它们会拷贝给 GameActivity.java。我的做法是在整个Gradle工程中搜索 <![CDATA[ 后面的字符串。
settings.gradle
经过Package执行后,settings.gradle的内容成为了:
rootProject.name='app'
include ':app'
include ':downloader_library'
include ':GCloud'
include ':GCloudCore'
include ':permission_library'
include ':PluginCrosCurl'
include ':TDM'
include ':TssSDK'
// generate mainobb assetpack
if (OBB_FILECOUNT.toInteger() > 0) {File obbfile = new File(OBB_FILE0)if (obbfile.exists()) {println 'Creating install-time assetpack for GooglePAD: assetpacks/install-time/obbassets'file("assetpacks/install-time/obbassets/src/main/assets").mkdirs()def assetBuildGradle = """apply plugin: 'com.android.asset-pack'assetPack {packName = "obbassets"dynamicDelivery {deliveryType = "install-time"instantDeliveryType = "install-time"}
}"""def assetBuildGradleFile = new File("assetpacks/install-time/obbassets/build.gradle")assetBuildGradleFile.write(assetBuildGradle)def destobbfile = new File("assetpacks/install-time/obbassets/src/main/assets/main.obb.png")if (destobbfile.exists()) {destobbfile.delete()}def srcobbStream = obbfile.newDataInputStream()def dstobbStream = destobbfile.newDataOutputStream()dstobbStream << srcobbStreamsrcobbStream.close()dstobbStream.close()}
}// add the assetpacks
def assetpacksDir = new File("assetpacks/install-time")
if (assetpacksDir.exists()) assetpacksDir.eachDir {println ':assetpacks:install-time:' + it.nameinclude ':assetpacks:install-time:' + it.name
}
assetpacksDir = new File("assetpacks/fast-follow")
if (assetpacksDir.exists()) assetpacksDir.eachDir {println ':assetpacks:fast-follow:' + it.nameinclude ':assetpacks:fast-follow:' + it.name
}
assetpacksDir = new File("assetpacks/on-demand")
if (assetpacksDir.exists()) assetpacksDir.eachDir {println ':assetpacks:on-demand:' + it.nameinclude ':assetpacks:on-demand:' + it.name
}
在这其中,我们看到特定的文件夹被设置成了开头我们说的“install-time”等拉取方式。
未完。
相关文章:
谷歌新安装包文件形式 .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) 定义: 低耦合指的是模块之间的依赖关系…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
