自 2025 年 3 月 27 日起,我们建议您使用 android-latest-release
而非 aosp-main
构建 AOSP 并为其做出贡献。如需了解详情,请参阅 AOSP 的变更。
APK 缓存
使用集合让一切井井有条
根据您的偏好保存内容并对其进行分类。
本文档介绍了如何设计 APK 缓存解决方案,以在支持 A/B 分区的设备上快速安装预加载的应用。
原始设备制造商 (OEM) 可以将预加载应用和热门应用放置在 APK 缓存中(对于采用 A/B 分区的新设备而言,这种缓存会存储在通常为空的 B 分区中),而且这样不会影响面向用户的任何数据空间。新设备或最近恢复出厂设置的设备上有 APK 缓存时,用户基本上可以立即开始使用,而无需从 Google Play 下载 APK 文件。
用例
- 将预加载应用存储在 B 分区,以实现更快捷的设置
- 将热门应用存储在 B 分区,以实现更快速的恢复
前提条件
要使用此功能,设备需要满足以下条件:
- 安装了 Android 8.1 (O MR1) 版本
- 实现了 A/B 分区
您只能在首次启动期间复制预加载内容。这是因为,在支持 A/B 系统更新的设备上,B 分区不会真正存储系统映像文件,而是存储预加载内容(例如零售演示模式资源、OAT 文件和 APK 缓存)。将资源复制到 /data 分区(此操作在首次启动期间完成)后,无线 (OTA) 更新就会将 B 分区用于下载系统映像的已更新版本。
因此,APK 缓存无法通过 OTA 进行更新;只能在出厂时预加载到设备上。恢复出厂设置只会影响 /data 分区。系统的 B 分区中仍会有预加载内容,直到系统下载 OTA 映像为止。恢复出厂设置后,系统会再次进行首次启动。这意味着,如果将 OTA 映像下载到 B 分区,然后将设备恢复出厂设置,那么 APK 缓存将无法使用。
实现
方法 1. system_other 分区上的内容
Pro:预加载内容不会在恢复出厂设置后丢失,系统会在重新启动后从 B 分区复制这些内容。
缺点:B 分区上需要有可用空间。在恢复出厂设置后进行启动时,需要额外时间来复制预加载内容。
为了在首次启动期间复制预加载内容,系统会调用 /system/bin/preloads_copy.sh
中的脚本。系统会通过单个参数(system_b
分区的只读装载点的路径)调用该脚本:
如需实现此功能,请完成以下特定于设备的更改。以下是 Marlin 设备的示例:
- 将执行复制操作的脚本添加到
device-common.mk
文件(在本示例中是 device/google/marlin/device-common.mk
),如下所示:
# Script that copies preloads directory from system_other to data partition
PRODUCT_COPY_FILES += \
device/google/marlin/preloads_copy.sh:system/bin/preloads_copy.sh
在以下位置查找示例脚本源代码:device/google/marlin/preloads_copy.sh
- 修改
init.common.rc
文件,以让其创建必要的 /data/preloads
目录和子目录:
mkdir /data/preloads 0775 system system
mkdir /data/preloads/media 0775 system system
mkdir /data/preloads/demo 0775 system system
在以下位置查找示例 init
文件源代码:device/google/marlin/init.common.rc
- 在文件
preloads_copy.te
中定义新的 SELinux 域:
type preloads_copy, domain, coredomain;
type preloads_copy_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(preloads_copy)
allow preloads_copy shell_exec:file rx_file_perms;
allow preloads_copy toolbox_exec:file rx_file_perms;
allow preloads_copy preloads_data_file:dir create_dir_perms;
allow preloads_copy preloads_data_file:file create_file_perms;
allow preloads_copy preloads_media_file:dir create_dir_perms;
allow preloads_copy preloads_media_file:file create_file_perms;
# Allow to copy from /postinstall
allow preloads_copy system_file:dir r_dir_perms;
在以下位置查找示例 SELinux 域文件:/device/google/marlin/+/main/sepolicy/preloads_copy.te
- 在新的
/sepolicy/file_contexts
文件中注册该域:
/system/bin/preloads_copy\.sh u:object_r:preloads_copy_exec:s0
在以下位置查找示例 SELinux 上下文的描述文件:device/google/marlin/sepolicy/preloads_copy.te
- 在构建时,您必须将具有预加载内容的目录复制到
system_other
分区:
# Copy contents of preloads directory to system_other partition
PRODUCT_COPY_FILES += \
$(call find-copy-subdir-files,*,vendor/google_devices/marlin/preloads,system_other/preloads)
这是 Makefile 中的更改示例,完成这项更改后,就可以将 APK 缓存资源从供应商的 Git 仓库(在本示例中是 vendor/google_devices/marlin/preloads)复制到 system_other 分区上的相应位置;稍后,在设备首次启动时,APK 缓存资源便会复制到 /data/preloads。此脚本会在编译时运行,以准备 system_other 映像。它希望将预加载内容放置到 vendor/google_devices/marlin/preloads 中。OEM 可以自由选择实际的代码库名称/路径。
- APK 缓存位于
/data/preloads/file_cache
中,布局如下:
/data/preloads/file_cache/
app.package.name.1/
file1
fileN
app.package.name.N/
这是设备上的最终目录结构。只要最终文件结构与上述结构相同,OEM 就可以自由选择任何实现方法。
方法 2. 在设备出厂时刷写的用户数据映像上的内容
此替代方式假设预加载内容已包含在 /data
分区上的 /data/preloads
目录中。
Pro:开箱即用,无需进行设备自定义即可在首次启动时复制文件。预加载内容已位于 /data
分区。
缺点:预加载内容会在恢复出厂设置后丢失。虽然这对部分 OEM 来说是可以接受的,但对在完成质量控制检查后要将设备恢复出厂设置的 OEM 来说,这种方法并不总是行得通。
将一种新的 @SystemApi 方法 getPreloadsFileCache()
添加到了 android.content.Context
。该方法会返回预加载缓存中某个应用专属目录的绝对路径。
添加了新方法 IPackageManager.deletePreloadsFileCache
,它允许删除预加载目录以回收所有空间。此方法只能由具有 SYSTEM_UID 的应用(即系统服务器或设置)进行调用。
应用准备
只有特权应用才可以访问预加载缓存目录。如需获得访问权限,应用必须安装在 /system/priv-app
目录中。
验证
- 首次启动后,设备的
/data/preloads/file_cache
目录中应该包含相关内容。 - 如果设备的存储空间不足,必须删除
file_cache/
目录中的内容。
使用示例 ApkCacheTest 应用测试 APK 缓存。
- 通过在根目录下运行以下命令来构建应用:
make ApkCacheTest
- 将应用安装为特权应用。(请注意,只有特权应用才能访问 APK 缓存。)
这需要一台已取得 root 权限的设备:
adb root && adb remount
adb shell mkdir /system/priv-app/ApkCacheTest
adb push $ANDROID_PRODUCT_OUT/data/app/ApkCacheTest/ApkCacheTest.apk /system/priv-app/ApkCacheTest/
adb shell stop && adb shell start
- 根据需要模拟文件缓存目录及其内容(此操作也需要 root 权限):
adb shell mkdir -p /data/preloads/file_cache/com.android.apkcachetest
adb shell restorecon -r /data/preloads
adb shell "echo "Test File" > /data/preloads/file_cache/com.android.apkcachetest/test.txt"
- 测试应用。安装应用并创建测试
file_cache
目录之后,打开 ApkCacheTest 应用。该应用中应该会显示一个 test.txt
文件及其内容。请参见以下屏幕截图,了解这些结果在界面上的显示方式。

图 1. ApkCacheTest 结果。
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2025-03-10。
[null,null,["最后更新时间 (UTC):2025-03-10。"],[],[],null,["# APK caching\n\nThis document describes design of an APK caching solution for rapid installation\nof preloaded apps on a device that supports A/B partitions.\n\n\nOEMs can place preloads and popular apps in the APK cache stored in the mostly\nempty B partition on new [A/B-partitioned](/docs/core/ota/ab_updates) devices without impacting\nany user-facing data space. By having an APK cache available on the device, new or\nrecently factory reset devices are ready for use almost immediately, without\nneeding to download APK files from Google Play.\n\nUse cases\n---------\n\n- Store preloaded apps in B partition for faster setup\n- Store popular apps in B partition for faster restoration\n\nPrerequisites\n-------------\n\n\nTo use this feature, the device needs:\n\n- Android 8.1 (O MR1) release installed\n- A/B partition implemented\n\n\nPreloaded content can be copied only during first boot. This is because on\ndevices supporting A/B system updates, the B partition doesn't actually store\nsystem image files, but instead preloaded content like retail demo resources,\nOAT files and the APK cache. After resources have been copied to the /data\npartition (this happens on first boot), the B partition will be used by [over-the-air (OTA)\nupdates](/docs/core/ota) for downloading updated versions of the system image.\n\n\nTherefore, the APK cache cannot be updated through OTA; it can be preloaded only\nat a factory. Factory reset affects only the /data partition. The system B\npartition still has the preloaded content until the OTA image is downloaded.\nAfter factory reset, the system will go through first boot again. This means APK\ncaching isn't available if the OTA image is downloaded to the B partition, and\nthen the device is factory reset.\n\nImplementation\n--------------\n\n### Approach 1. Content on\nsystem_other partition\n\n\n**Pro**: Preloaded content isn't lost after factory reset - it\nwill be copied from the B partition after a reboot.\n\n\n**Con**: Requires space on B partition. Boot after factory reset\nrequires additional time to copy preloaded content.\n\n\nIn order for preloads to be copied during first boot, the system calls a script\nin `/system/bin/preloads_copy.sh`. The script is called with a single\nargument (path to the read-only mount point for `system_b`\npartition):\n\n\nTo implement this feature, make these device-specific changes. Here is an\nexample from Marlin:\n\n1. Add the script that does the copying to the `device-common.mk` file (in this case, `device/google/marlin/device-common.mk`), like so: \n\n ```\n # Script that copies preloads directory from system_other to data partition\n PRODUCT_COPY_FILES += \\\n device/google/marlin/preloads_copy.sh:system/bin/preloads_copy.sh\n ```\n Find example script source at: [device/google/marlin/preloads_copy.sh](https://android.googlesource.com/device/google/marlin/+/android16-release/preloads_copy.sh)\n2. Edit the `init.common.rc` file to have it create the necessary` /data/preloads` directory and subdirectories: \n\n mkdir /data/preloads 0775 system system\n mkdir /data/preloads/media 0775 system system\n mkdir /data/preloads/demo 0775 system system\n\n Find example `init` file source at: [device/google/marlin/init.common.rc](https://android.googlesource.com/device/google/marlin/+/android16-release/init.common.rc)\n3. Define a new SELinux domain in the file `preloads_copy.te`: \n\n ```\n type preloads_copy, domain, coredomain;\n type preloads_copy_exec, exec_type, vendor_file_type, file_type;\n\n init_daemon_domain(preloads_copy)\n\n allow preloads_copy shell_exec:file rx_file_perms;\n allow preloads_copy toolbox_exec:file rx_file_perms;\n allow preloads_copy preloads_data_file:dir create_dir_perms;\n allow preloads_copy preloads_data_file:file create_file_perms;\n allow preloads_copy preloads_media_file:dir create_dir_perms;\n allow preloads_copy preloads_media_file:file create_file_perms;\n\n # Allow to copy from /postinstall\n allow preloads_copy system_file:dir r_dir_perms;\n ```\n Find an example SELinux domain file at: [/device/google/marlin/+/android16-release/sepolicy/preloads_copy.te](https://android.googlesource.com/device/google/marlin/+/android16-release/sepolicy/preloads_copy.te)\n4. Register the domain in a new /sepolicy/file_contexts file: \n\n ```\n /system/bin/preloads_copy\\.sh u:object_r:preloads_copy_exec:s0\n ```\n Find an example SELinux contexts file at: [device/google/marlin/sepolicy/preloads_copy.te](https://android.googlesource.com/device/google/marlin/+/android16-release/sepolicy/preloads_copy.te)\n5. At build time, the directory with preloaded content must be copied to the `system_other` partition: \n\n ```\n # Copy contents of preloads directory to system_other partition\n PRODUCT_COPY_FILES += \\\n $(call find-copy-subdir-files,*,vendor/google_devices/marlin/preloads,system_other/preloads)\n ```\n This is an example of a change in a Makefile that allows copying APK cache resources from vendor's Git repository (in our case it was vendor/google_devices/marlin/preloads) to the location on system_other partition that will later be copied to /data/preloads when device boots for the first time. This script runs at build time to prepare system_other image. It expects preloaded content to be available in vendor/google_devices/marlin/preloads. OEM is free to choose the actual repository name/path.\n6. The APK cache is located in `/data/preloads/file_cache` and has the following layout: \n\n ```\n /data/preloads/file_cache/\n app.package.name.1/\n file1\n fileN\n app.package.name.N/\n ```\n This is the final directory structure on the devices. OEMs are free to choose any implementation approach as long as the final file structure replicates the one described above.\n\n### Approach 2. Content on user data\nimage flashed at factory\n\n\nThis alternative approach assumes that preloaded content is already included in\nthe `/data/preloads` directory on the `/data` partition.\n\n\n**Pro** : Works out of the box - no need to make device\ncustomizations to copy files on first boot. Content is already on the\n`/data` partition.\n\n\n**Con**: Preloaded content is lost after a factory reset. While\nthis might be acceptable for some, it might not always work for OEMs who factory\nreset devices after doing quality control inspections.\n\n\nA new @SystemApi method, `getPreloadsFileCache()`, was added to\n`android.content.Context`. It returns an absolute path to an\napp-specific directory in the preloaded cache.\n\n\nA new method, `IPackageManager.deletePreloadsFileCache`, was added\nthat allows deleting the preloads directory to reclaim all space. The method can\nbe called only by apps with SYSTEM_UID, i.e. system server or Settings.\n\nApp preparation\n---------------\n\n\nOnly privileged apps can access the preloads cache directory. For that\naccess, apps must be installed in the` /system/priv-app` directory.\n\nValidation\n----------\n\n- After first boot, the device should have content in the `/data/preloads/file_cache` directory.\n- The content in the `file_cache/` directory must be deleted if the device runs low on storage.\n\nUse the example [ApkCacheTest](https://android.googlesource.com/platform/development/+/android16-release/samples/apkcachetest/)\napp for testing APK cache.\n\n1. Build the app by running this command from the root directory: \n\n make ApkCacheTest\n\n2. Install the app as a privileged app. (Remember, only privileged apps can access the APK cache.) This requires a rooted device: \n\n adb root && adb remount\n adb shell mkdir /system/priv-app/ApkCacheTest\n adb push $ANDROID_PRODUCT_OUT/data/app/ApkCacheTest/ApkCacheTest.apk /system/priv-app/ApkCacheTest/\n adb shell stop && adb shell start\n\n3. Simulate the file cache directory and its content if needed (also requiring root privileges): \n\n adb shell mkdir -p /data/preloads/file_cache/com.android.apkcachetest\n adb shell restorecon -r /data/preloads\n adb shell \"echo \"Test File\" \u003e /data/preloads/file_cache/com.android.apkcachetest/test.txt\"\n\n4. Test the app. After installing the app and creating test `file_cache` directory, open the ApkCacheTest app. It should show one file `test.txt` and its contents. See this screenshot to see how these results appear in the user interface.\n\n **Figure 1.** ApkCacheTest results."]]