SDK 运行时的向后兼容性

提供反馈

本文档提出了一个新的 Jetpack 库,以帮助开发者迁移到 SDK 运行时。它解释了以前的 Android 平台版本(从构建到执行)将如何支持 SDK 运行时,以及开发者可以预见运行时环境中有哪些差异或限制。借助此库,开发者可为其应用或 SDK 创建单个版本,并使该版本在支持或不支持 SDK 运行时的设备上都能正常运行。

向后兼容性是通过以下组件实现的:

  • Android Gradle 插件 (AGP) + Bundletool 通过将 SDK 运行时捆绑到 APK 中,为不支持 SDK 运行时的设备构建应用变体。

  • SDK 运行时客户端库 (androidx.privacysandbox.sdkruntime:sdkruntime-client) 从应用资源加载捆绑的 SDK,并在不支持 SDK 运行时的设备上模拟 SDK 运行时。

  • SDK 运行时提供方库 (androidx.privacysandbox.sdkruntime:sdkruntime-provider) 为 SDK 提供了一个允许从 SDK 运行时客户端库加载的 API。

使用 Bundletool 提供 SDK

在支持 SDK 运行时的设备上,SDK 将作为单独的软件包提供和安装。

为了支持缺少 SDK 运行时支持的平台版本,Bundletool 会为应用 APK 集构建一个或多个变体并使其包含应用依赖的所有 SDK。每个 SDK 都会打包为单独的 APK 分块。此外,还会执行以下转换:

  1. 将 SDK 字节码 (DEX) 文件作为资源复制到 SDK 分块中。
  2. 将 SDK Java 资源作为资源复制到 SDK 分块中。
  3. 重新映射 SDK 资源,并将其与应用资源合并。
  4. 为 SDK 运行时客户端库生成配置。

使用 SDK 运行时客户端库加载 SDK

SDK 运行时客户端库提供与平台 API 类似的 API,但支持 SDK 运行时环境中的 SDK 以及与变体应用捆绑的 SDK。

如需使用 SDK 运行时客户端库,请添加依赖项 androidx.privacysandbox.sdkruntime:sdkruntime-client,并使用 SdkSandboxManagerCompat 而非 SdkSandboxManager

当应用尝试加载 SDK 时,该库会先检查 SDK 在构建期间是否与应用捆绑到一起。如果捆绑了,该库会从 SDK 分块中提取 SDK 并将其加载到应用进程中。如果 SDK 未与应用捆绑,该库会委托平台 API 加载 SDK。

从资源中提取 SDK

当应用尝试加载捆绑的 SDK 时,SDK 运行时客户端库会检查 SDK 的 DEX 文件是否已提取到设备存储空间 (code_cache),如果尚未提取,则会从资源中提取这些文件。

在应用安装或更新后,该库通常只会提取 1 次文件。

如果可用存储空间小于允许的阈值(目前为 100 MB),并且未提取任何 DEX 文件,该库会尝试直接从受支持设备(API 27 及更高级别)上的资源加载 SDK。这会导致内存占用量变大。

适用于 SDK 类的类加载器

为避免 SDK 和应用类之间发生冲突,所有 SDK 类均使用完全独立于主应用类加载器的单独类加载器进行加载。

在当前的 SDK 运行时设计中,应用与 SDK 之间的所有通信均使用 Binder IPC 调用进行。捆绑的 SDK 也使用相同的 SDK Binder 对象,而 Binder 事务序列化允许应用开发者将 SDK Binder 对象转换为应用端的 SDK Binder 接口。

对于其他内部交互(例如初始化 SDK、向 SDK 提供控制器 API 等),该库使用反射和动态代理跨不同的类加载器运行。

SDK 环境

SDKRuntime 提供方库为 SDK 开发者提供 API。这些 API 与平台 API 类似,但允许 SDK 运行时环境和 SDKRuntime 客户端库加载 SDK。

为了能够使用库 SDK,您需要添加 androidx.privacysandbox.sdkruntime:sdkruntime-provider 依赖项并扩展 SandboxedSdkProviderCompat 而非 SandboxedSdkProvider

您还需要使用 SandboxedSdkProviderAdapter 作为 SDK 提供方,以便在 SDK 运行时环境中加载兼容型提供方。

在 SDK 运行时环境中加载 SDK 时,SdkSandboxControllerCompat 会委托给平台 API;当 SDK 作为捆绑的 SDK 加载时,会委托给 SDKRuntime 客户端库。

对于捆绑的 SDK,该库会以模拟与 SDK 运行时环境类似的行为方式修改 SDK 环境。

以下部分将介绍 SDKRuntime 客户端库加载 SDK 时的预期行为。

SDK 资源

在应用进程中加载 SDK 时,支持 SDK 资源 (res/)。Bundletool 会将所有 SDK 的资源与应用资源合并。

为避免冲突,系统会通过更改所有资源 ID 中的 packageId 前缀来重新映射 SDK 资源。

当 SDKRuntime 客户端库加载 SDK 时,运行时环境中的 packageId 会更新,以允许使用 R 类处理重新映射的资源。

Java 资源

在应用进程中加载 SDK 时,支持 Java 资源。Bundletool 会将所有 SDK Java 资源复制到应用资源中的一个特殊目录下。SDKRuntime 客户端库使用中间类加载器将所有与 Java 资源相关的调用重定向到新的根目录。

SDK 资源

SDK 资源无需经过重新映射就能与应用资源合并。

SDK 存储

为了支持 SDK Storage,SDK 运行时客户端库会为应用存储空间中的每个捆绑 SDK 创建一个专用根目录,并提供一个将此目录用作存储根目录的特殊情境。

此情境可从 SandboxedSdkProviderCompat#getContext 中检索。

支持的存储相关方法:

  • getDataDir
  • getCacheDir
  • getCodeCacheDir
  • getNoBackupFilesDir
  • getDir
  • getFilesDir
  • openFileInput
  • openFileOutput
  • deleteFile
  • getFileStreamPath
  • fileList
  • getDatabasePath
  • openOrCreateDatabase
  • moveDatabaseFrom - 仅在 SDK 情境之间
  • deleteDatabase
  • databaseList
  • getSharedPreferences
  • moveSharedPreferencesFrom - 仅在 SDK 情境之间
  • deleteSharedPreferences

可以通过针对此情境调用 createDeviceProtectedStorageContext() 来创建一种能保护设备的存储情境。

SdkSandboxControllerCompat

SDKRuntime 客户端库可为应用进程中加载的捆绑 SDK 提供 SdkSandboxControllerCompat 实现。

如果客户端库不支持 API(例如使用比应用版本更新的库版本构建的 SDK),系统将使用最合适的回退策略(免运维或异常)。

版本控制

当 SDKRuntime 客户端库加载捆绑的 SDK 时,它会与 SDK 内的 SDKRuntime 提供方库执行握手。在握手期间,库会交换其版本并调整行为,以将不可用的 API 替换为最合适的回退策略(免运维或异常)。

强烈建议应用开发者和 SDK 开发者都使用最新版本的库,否则那些需要双方都提供支持的功能可能无法发挥作用。

任何版本的 SDKRuntime 客户端库都可以使用任何版本的 SDKRuntime 提供方库加载 SDK,反之亦然。

未来会改用为通过特定版本的提供方库加载 SDK 而需使用的最低客户端库版本。

这样可以最大限度减少分片化,并确保在捆绑的 SDK 成功加载后大多数 API 都受支持。