创建 Android 库

Android 库的结构与 Android 应用模块相同。它可以包含编译应用所需的一切内容,包括源代码、资源文件和 Android 清单。不过,Android 库会编译到可供您用作 Android 应用模块依赖项的 Android ARchive (AAR) 文件中,而不是编译到在设备上运行的 APK 中。与 JAR 文件不同,AAR 文件可以包含多项 Android 资源和一个清单文件,让您除了能够在 Java 类和方法中进行捆绑以外,还能够在布局和可绘制对象等共享资源中进行捆绑。

库模块在以下情况下非常有用:

  • 当您要编译多个使用某些相同的组件(例如 Activity、服务或界面布局)的应用时。
  • 当您要编译存在于多个 AKP 变体中的应用时(例如免费版本和付费版本,并且您需要在这两个版本中使用相同的核心组件)。

无论在哪种情况下,您都只需将想要重复使用的文件移到库模块中,然后将库添加为每个应用模块的依赖项。本页将向您介绍如何执行这两个操作。

创建库模块

要在您的项目中创建新的库模块,请按以下步骤操作:

  1. 依次点击 File > New > New Module
  2. 在随即显示的 Create New Module 窗口中,依次点击 Android LibraryNext

    还有一个用于创建 Java 库的选项,它可以编译传统的 JAR 文件。虽然 JAR 文件对很多项目(尤其是当您想要与其他平台共享代码时)都很有用,但它不允许您包含 Android 资源或清单文件,而要在 Android 项目中重复使用代码,Android 资源和清单文件是非常有用的。因此,本指南侧重于介绍如何创建 Android 库。

  3. 为您的库命名,并为库中的代码选择一个最低的 SDK 版本,然后点击 Finish

在 Gradle 项目同步完成后,库模块会显示在左侧的 Project 面板中。如果您没有看到新模块文件夹,请确保其显示的是 Android 视图

将应用模块转换为库模块

如果现有的应用模块包含您想要重复使用的所有代码,您可以按照以下步骤将其转换为库模块:

  1. 打开模块级 build.gradle 文件。
  2. 删除 applicationId 行。只有 Android 应用模块才能定义此行。
  3. 在文件的顶部,您应该看到以下代码:
        apply plugin: 'com.android.application'
        

    将其更改为以下代码:

        apply plugin: 'com.android.library'
        
  4. 保存文件,然后依次点击 File > Sync Project with Gradle Files

大功告成。模块的整个结构仍然相同,但是现在它会作为 Android 库运行,编译现在也会创建 AAR 文件,而不是 APK。

如果您想要编译 AAR 文件,请在 Project 窗口中选择库模块,然后依次点击 Build > Build APK

将库添加为依赖项

要在另一个应用模块中使用您的 Android 库代码,请按以下步骤操作:

  1. 通过以下其中一种方式将相应的库添加到您的项目(如果库模块是您在同一个项目中创建的,那么它就已经存在了,因此您可以跳过此步骤):
    • 添加已编译的 AAR(或 JAR)文件(库必须已编译):
      1. 依次点击 File > New > New Module
      2. 依次点击 Import .JAR/.AAR PackageNext
      3. 输入已编译的 AAR 或 JAR 文件的位置,然后点击 Finish
    • 将库模块导入到您的项目(库源代码文件会成为项目的一部分):
      1. 依次点击 File > New > Import Module
      2. 输入库模块目录的位置,然后点击 Finish

      此时,库模块便复制到您的项目中了,因此您可以实际修改库代码。如果您想要维护单个版本的库代码,那么您或许不应该执行此操作,而是应该添加已编译的 AAR 文件(如上所述)。

  2. 确保库列在 settings.gradle 文件的顶部,如下面名为“my-library-module”的库所示:
        include ':app', ':my-library-module'
        
  3. 打开应用模块的 build.gradle 文件,然后将一行新代码添加到 dependencies 块,如以下代码段所示:
        dependencies {
            implementation project(":my-library-module")
        }
        
  4. 点击 Sync Project with Gradle Files

在以上示例中,implementation 配置会将名为 my-library-module 的库添加为整个应用模块的编译依赖项。而如果只想将此库用于特定的编译变体,您应该使用 buildVariantNameImplementation,而不是 implementation。例如,如果您只想在“pro”产品特性中包含此库,则所需代码如下所示:

    productFlavors {
        pro { ... }
    }
    dependencies {
        proImplementation project(":my-library-module")
    }
    

您的应用模块现在可以访问 Android 库中的任何代码和资源,并且库 AAR 文件在编译时已捆绑到您的 APK 中。

不过,如果想要单独共享 AAR 文件,您可以在 project-name/module-name/build/outputs/aar/ 中找到它,也可以通过点击 Build > Make Project 的方式重新生成此文件。

注意:要详细了解依赖项管理,请参阅使用变体感知依赖项管理

选择要设为公开的资源

库中的所有资源在默认情况下均处于公开状态。要将所有资源隐式设为私有,您必须至少将一个特定属性定义为公开。资源包括您项目的 res/ 目录中的所有文件,例如图片。要防止库的用户访问仅供内部使用的资源,您应该通过声明一个或多个公开资源的方式来使用这种自动私有标识机制。或者,您也可以通过添加空的 <public /> 标记将所有资源设为私有,此标记不会将任何资源设为公开,而是会将一切(所有资源)都设为私有。

要声明公开资源,请向库的 public.xml 文件添加 <public> 声明。如果之前未添加过公开资源,您需要在库的 res/values/ 目录中创建 public.xml 文件。

以下示例代码会创建两个名称分别为 mylib_app_namemylib_public_string 的公开字符串资源:

    <resources>
        <public name="mylib_app_name" type="string"/>
        <public name="mylib_public_string" type="string"/>
    </resources>
    

如果想要让任何资源对使用您的库的开发者可见,您应该将这些资源设为公开。

通过将属性隐式设为私有,您不仅可以防止库的用户从内部库资源获得代码完成建议,还可以重命名或移除私有资源,而不会破坏库的客户端。系统会将代码完成从私有资源中过滤出去,并且 Lint 会在您尝试引用私有资源时发出警告。

在编译库时,Android Gradle 插件会获取公开资源定义,并将其提取到 public.txt 文件中,然后系统会将此文件打包到 AAR 文件中。

库模块开发注意事项

在开发库模块和相关应用时,请注意以下行为和限制。

向 Android 应用模块添加对库模块的引用后,您可以设置它们的相对优先级。在编译时,库会按照优先级由低到高的顺序逐一与应用合并。

  • 资源合并冲突

    编译工具会将库模块中的资源与相关应用模块的资源合并。如果这两个模块中都定义了给定的资源 ID,系统会使用应用中的资源。

    如果多个 AAR 库之间发生冲突,系统会使用依赖项列表中首先列出的库(靠近 dependencies 顶部)中的资源。

    为了避免常用的资源 ID 发生资源冲突,请考虑使用对模块具有唯一性(或在所有项目模块之间具有唯一性)的前缀或其他一致的命名方案。

  • 在多模块编译中,系统会将 JAR 依赖项视为传递依赖项

    在向输出 AAR 的库项目添加 JAR 依赖项时,JAR 会由库模块进行处理,并与其 AAR 打包在一起。

    不过,如果您的项目包含库模块,并且此模块已被应用模块使用,应用模块便会将库的本地 JAR 依赖项视为传递依赖项。在这种情况下,本地 JAR 将由使用它的应用模块进行处理,而不是由库模块进行处理。这是为了加快库代码更改导致的增量编译的速度。

    由本地 JAR 依赖项导致的所有 Java 资源冲突都必须在使用相应库的应用模块中解决。

  • 库模块可以依赖于外部 JAR 库

    您可以开发一个依赖于外部库(例如 Google 地图外部库)的库模块。在这种情况下,相关应用必须针对包含此外部库的目标(例如 Google API 插件)进行编译。另请注意,库模块和相关应用都必须在其清单文件的 <uses-library> 元素中声明此外部库。

  • 应用模块的 minSdkVersion 必须大于或等于库定义的版本

    库是作为相关应用模块的一部分进行编译的,因此,库模块中使用的 API 必须与应用模块支持的平台版本兼容。

  • 每个库模块都会创建自己的 R 类

    在您编译相关应用模块时,库模块会先编译到 AAR 文件中,然后再添加到应用模块中。因此,每个库都有自己的 R 类,并根据库的软件包名称命名。所需的所有软件包中都会创建从主模块和库模块生成的 R 类,包括主模块的软件包和库的软件包。

  • 库模块可以包含自己的 ProGuard 配置文件

    如果有用于编译和发布 AAR 的库项目,您可以向库的编译配置添加 ProGuard 配置文件,并且 Android Gradle 插件规则适用于您指定的 ProGuard 规则。编译工具会将此文件嵌入到为库模块生成的 AAR 文件中。在您将库添加到应用模块后,库的 ProGuard 文件会附加到应用模块的 ProGuard 配置文件 (proguard.txt)。

    通过将 ProGuard 文件嵌入到库模块中,您可以确保依赖于相应库的应用模块不必手动更新其 ProGuard 文件即可使用此库。当 ProGuard 在 Android 应用模块上运行时,它会同时使用来自应用模块和库的指令,因此您不应该只在库上运行 ProGuard。

    要向库项目添加 ProGuard 规则,您必须使用库的 build.gradle 文件的 defaultConfig 块内的 consumerProguardFiles 属性指定文件名称。例如,以下代码段会将 lib-proguard-rules.txt 设为库的 ProGuard 配置文件:

        android {
            defaultConfig {
                consumerProguardFiles 'lib-proguard-rules.txt'
            }
            ...
        }
        

    不过,如果库模块是要编译到 APK 中的多模块编译的一部分,并且不会生成 AAR,您应该只在使用相应库的应用模块上运行 ProGuard。要详细了解 ProGuard 规则及其用法,请参阅压缩代码和资源

  • 测试库模块的方法与测试应用的方法相同。

    主要区别在于,库及其依赖项会作为测试 APK 的依赖项自动包含在内。这意味着测试 APK 不仅包含自己的代码,还包含库的 AAR 及其所有依赖项。由于没有单独的“被测应用”,因此 androidTest 任务只会安装(和卸载)测试 APK。

    合并多个清单文件时,Gradle 会遵循默认的优先级顺序,并将库的清单合并到测试 APK 的主清单中。

AAR 文件详解

AAR 文件的文件扩展名为 .aar,Maven 工件类型应该也是 aar。此文件本身是一个 zip 文件,其中包含以下强制性条目:

  • /AndroidManifest.xml
  • /classes.jar
  • /res/
  • /R.txt
  • /public.txt

此外,AAR 文件可能包含以下一个或多个可选条目:

  • /assets/
  • /libs/name.jar
  • /jni/abi_name/name.so(其中“abi_name”是 Android 支持的 ABI 之一)
  • /proguard.txt
  • /lint.jar
  • /api.jar