实现 Java SDK 库

Android 平台包含大量的共享 Java 库,如果需要,可以在应用清单中使用 <uses-library> 标记将它们纳入应用的类路径中。应用与这些库相关联,因此在兼容性、API 审核和工具支持方面,应将其与其他 Android API 一样对待。但请注意,大多数库都没有这些功能。

java_sdk_library 模块类型有助于管理此类库。设备制造商可以将此机制用于自己的共享 Java 库,以保持其 API 的向后兼容性。如果设备制造商通过 <uses-library> 标记(而不是 bootclass 路径)使用自己的共享 Java 库,则 java_sdk_library 可以验证这些 Java 库是否能让 API 保持稳定。

java_sdk_library 会实现供应用使用的可选 SDK API。通过构建文件 (Android.bp) 中的 java_sdk_library 实现的库会执行以下操作:

  • 生成存根库,将 stubsstubs.systemstubs.test 包括在内。这些存根库是通过识别 @hide@SystemApi@TestApi 注释创建的。
  • java_sdk_library 用于管理 API 子目录中的 API 规范文件(例如 current.txt)。系统会根据最新代码对这些文件进行检查,确保它们是最新版本。如果不是最新版本,您会收到一条错误消息,告知您如何更新它们。手动查看所有更新更改,以确保它们符合您的预期。

    如需更新所有 API,请使用 m update-api。如需验证 API 是否为最新版本,请使用 m checkapi
  • 系统会对照最近发布的 Android 版本对 API 规范文件进行检查,以确保相应 API 向后兼容早期版本。AOSP 中提供的 java_sdk_library 模块会将之前发布版本放置到 prebuilts/sdk/<latest number> 下。
  • 对于 API 规范文件检查,您可以执行以下三项操作之一
    • 允许检查继续。(无需执行任何操作。)
    • 将以下代码添加到 java_sdk_library 中来停用检查:
      unsafe_ignore_missing_latest_api: true,
    • 通过在 version/scope/api 目录中创建一个名为 module_name.txt 的空文本文件,为新的 java_sdk_library 模块提供空 API。
  • 如果安装了运行时的实现库,就会生成并安装 XML 文件。

java_sdk_library 的工作原理

名为 Xjava_sdk_library 会创建以下内容:

  1. 实现库的两个副本:一个名为 X,另一个名为 X.impl。库 X 已安装在设备上。仅当其他模块需要显式访问实现库(例如用于测试)时,才会显示库 X.impl。请注意,需要显式访问的情况较少。
  2. 您可以启用或停用范围以自定义访问权限。(与 Java 关键字访问修饰符类似,公开范围可提供广泛的访问权限;测试范围包含仅用于测试中的 API。) 该库会为每种启用的范围创建以下内容:
    • 桩来源模块(属于 droidstubs 模块类型)- 使用实现来源,并输出一组桩来源以及 API 规范文件。
    • 存根库(属于 java_library 模块类型)- 存根的编译版本。用于编译此版本的库与为 java_sdk_library 提供的库不同,这可以确保实现细节不会泄露到 API 存根中。
    • 如果您需要额外的库来编译桩,请使用 stub_only_libsstub_only_static_libs 属性提供这些库。

如果 java_sdk_library 名为“X”,并且被编译为“X”,请始终以这种方式引用它,并且不要进行修改。构建流程将选择合适的库。为了确保您拥有最合适的库,请检查您的存根,看看相应构建流程是否引入了错误。请按照本指南进行必要的更正:

  • 通过查询命令行并检查其中列出了哪些存根以确定您的范围,验证您是否拥有合适的库:
    • 范围过于宽泛:所需的库需要一定范围的 API。但您发现库中包含的 API 不在相应范围内,例如公共 API 中包含的系统 API。
    • 范围过窄:所需的库无法访问部分必需的库。例如,依赖的库需要使用系统 API,但只能获取公共 API。因为缺少所需的 API,这通常会导致编译错误。
  • 如需修复该库,请仅执行下列操作之一
    • 更改 sdk_version 以选择所需的版本;或
    • 显式指定相应的库,例如 <X>.stubs<X>.stubs.system

java_sdk_library X 的用法

实现库 X 会在从 apex.java_libs 引用后被使用。不过,由于 Soong 限制,系统从同一 APEX 库中的另一个 java_sdk_library 模块引用库 X 时,必须显式使用 X.impl,而非库 X

从其他位置引用 java_sdk_library 时,系统会使用存根库。存根库的选择基于相应模块的 sdk_version 属性设置。例如,指定 sdk_version: "current" 的模块会使用公共存根,而指定 sdk_version: "system_current" 的模块会使用系统存根。如果找不到完全匹配的存根,则会使用最接近的存根库。仅提供公共 API 的 java_sdk_library 将为所有人提供公共存根。

使用 Java SDK 库的构建流程
图 1. 使用 Java SDK 库的构建流程

示例和来源

java_sdk_library 中必须提供 srcsapi_packages 属性。

java_sdk_library {
        name: "com.android.future.usb.accessory",
        srcs: ["src/**/*.java"],
        api_packages: ["com.android.future.usb"],
    }

AOSP 建议(但不要求)新的 java_sdk_library 实例显式启用要使用的 API 范围。您还可以(可选)迁移现有的 java_sdk_library 实例,以显式启用相应实例将使用的 API 范围:

java_sdk_library {
         name: "lib",
         public: {
           enabled: true,
         },
         system: {
           enabled: true,
         },
         …
    }

如需配置用于运行时的 impl 库,请使用所有常规 java_library 属性,例如 hostdexcompile_dexerrorprone

java_sdk_library {
        name: "android.test.base",

        srcs: ["src/**/*.java"],

        errorprone: {
          javacflags: ["-Xep:DepAnn:ERROR"],
        },

        hostdex: true,

        api_packages: [
            "android.test",
            "android.test.suitebuilder.annotation",
            "com.android.internal.util",
            "junit.framework",
        ],

        compile_dex: true,
    }

如需配置存根库,请使用以下属性:

  • merge_annotations_dirsmerge_inclusion_annotations_dirs
  • api_srcs:属于 API 但不属于运行时库的可选源文件的列表。
  • stubs_only_libs:构建存根时位于类路径中的 Java 库的列表。
  • hidden_api_packages:必须对 API 隐藏的软件包名称的列表。
  • droiddoc_optionsmetalava 的附加参数。
  • droiddoc_option_files:列出可以使用 $(location <label>)droiddoc_options 中引用的文件,其中 <file> 是列表中的条目。
  • annotations_enabled

java_sdk_library 是一个 java_library,但不是 droidstubs 模块,因此不支持部分 droidstubs 属性。以下示例来自 android.test.mock 库 build 文件。

java_sdk_library {
        name: "android.test.mock",

        srcs: [":android-test-mock-sources"],
        api_srcs: [
            // Note: The following aren’t APIs of this library. Only APIs under the
            // android.test.mock package are taken. These do provide private APIs
            // to which android.test.mock APIs reference. These classes are present
            // in source code form to access necessary comments that disappear when
            // the classes are compiled into a Jar library.
            ":framework-core-sources-for-test-mock",
            ":framework_native_aidl",
        ],

        libs: [
            "framework",
            "framework-annotations-lib",
            "app-compat-annotations",
            "Unsupportedappusage",
        ],

        api_packages: [
            "android.test.mock",
        ],
        permitted_packages: [
            "android.test.mock",
        ],
        compile_dex: true,
        default_to_stubs: true,
    }

保持向后兼容性

构建系统通过在构建时将最新的 API 文件与生成的 API 文件进行比较来检查 API 是否保持了向后兼容性。java_sdk_library 使用 prebuilt_apis 提供的信息执行兼容性检查。 使用 java_sdk_library 构建的所有库都必须在 prebuilt_apis 中的拥有最新版的 api_dirs 中的 API 文件。 当您发布版本时,可以使用 PRODUCT=sdk_phone_armv7-sdk 通过 dist build 获取 API 列表文件和存根库。

api_dirs 属性是 prebuilt_apis 中的 API 版本目录的列表。API 版本目录必须位于 Android.bp 目录级别。

prebuilt_apis {
       name: "foo",
       api_dirs: [
           "1",
           "2",
             ....
           "30",
           "current",
       ],
    }

在 prebuilts 目录下配置 version/scope/api/ 结构的目录。version 与 API 级别相对应,而 scope 可指定是公开目录、系统目录还是测试目录。

  • version/scope 包含 Java 库。
  • version/scope/api 包含 API .txt 文件。在此处创建名为 module_name.txtmodule_name-removed.txt 的空文本文件。
     ├── 30
          │   ├── public
          │   │   ├── api
          │   │   │   ├── android.test.mock-removed.txt
          │   │   │   └── android.test.mock.txt
          │   │   └── android.test.mock.jar
          │   ├── system
          │   │   ├── api
          │   │   │   ├── android.test.mock-removed.txt
          │   │   │   └── android.test.mock.txt
          │   │   └── android.test.mock.jar
          │   └── test
          │       ├── api
          │       │   ├── android.test.mock-removed.txt
          │       │   └── android.test.mock.txt
          │       └── android.test.mock.jar
          └── Android.bp