将 Gradle 关联到您的原生库

要将您的原生库项目作为 Gradle 构建依赖项包含在内,您需要向 Gradle 提供 CMake 或 ndk-build 脚本文件的路径。当您构建应用时,Gradle 会运行 CMake 或 ndk-build,并将生成的共享库打包到您的应用中。Gradle 还会利用该构建脚本来确定哪些文件需要引入到您的 Android Studio 项目中,以便您可以在项目 (Project) 窗口中访问它们。如果您还没有用于原生源文件的构建脚本,则需要先创建一个 CMake 构建脚本,然后再继续。

Android 项目中的每个模块只能关联到一个 CMake 或 ndk-build 脚本文件。例如,如果您想构建并打包来自多个 CMake 项目的输出,则需要使用一个 CMakeLists.txt 文件作为您的顶级 CMake 构建脚本(然后将 Gradle 关联到该脚本),并添加其他 CMake 项目作为该构建脚本的依赖项。同样,如果您使用的是 ndk-build,则可以在顶级 Android.mk 脚本文件中包含其他 Makefile

一旦将 Gradle 关联到原生项目,Android Studio 就会更新项目 (Project) 面板,在 cpp 组中显示您的源文件和原生库,并在 External Build Files 组中显示您的外部构建脚本。

注意:在更改 Gradle 配置时,请务必点击工具栏中的同步项目 (Sync Project) 以应用更改。此外,在将 CMake 或 ndk-build 脚本文件关联到 Gradle 后,如果您对其进行了更改,则应通过从菜单栏选择构建 (Build) > 刷新关联的 C++ 项目 (Refresh Linked C++ Projects) 来使 Android Studio 同步这些更改。

您可以使用 Android Studio 界面将 Gradle 关联到外部 CMake 或 ndk-build 项目。

  1. 从 IDE 左侧打开项目 (Project) 面板,然后选择 Android 视图。
  2. 右键点击您想要关联到原生库的模块(例如 app 模块),然后从菜单中选择 Link C++ Project with Gradle。您应该会看到一个类似于图 4 所示的对话框。
  3. 从下拉菜单中选择 CMakendk-build
    1. 如果您选择 CMake,请使用 项目路径 (Project Path) 旁边的字段指定您的外部 CMake 项目的 CMakeLists.txt 脚本文件。
    2. 如果您选择 ndk-build,请使用 项目路径 (Project Path) 旁边的字段指定您的外部 ndk-build 项目的 Android.mk 脚本文件。如果 Application.mk 文件与您的 Android.mk 文件位于同一目录下,Android Studio 也会将其包含在内。

    图 4. 使用 Android Studio 对话框关联外部 C++ 项目。

  4. 点击 OK

手动配置 Gradle

要手动配置 Gradle 以关联到您的原生库,您需要将 externalNativeBuild 代码块添加到模块级的 build.gradle 文件中,并使用 cmake ndkBuild 代码块对其进行配置。

Groovy

android {
  ...
  defaultConfig {...}
  buildTypes {...}

  // Encapsulates your external native build configurations.
  externalNativeBuild {

    // Encapsulates your CMake build configurations.
    cmake {

      // Provides a relative path to your CMake build script.
      path "CMakeLists.txt"
    }
  }
}

Kotlin

android {
  ...
  defaultConfig {...}
  buildTypes {...}

  // Encapsulates your external native build configurations.
  externalNativeBuild {

    // Encapsulates your CMake build configurations.
    cmake {

      // Provides a relative path to your CMake build script.
      path = file("CMakeLists.txt")
    }
  }
}

注意:如果您想将 Gradle 关联到现有的 ndk-build 项目,请使用 ndkBuild 代码块而不是 cmake 代码块,并提供指向您的 Android.mk 文件的相对路径。如果 Application.mk 文件与您的 Android.mk 文件位于同一目录下,Gradle 也会将其包含在内。

指定可选配置

您可以通过在模块级 build.gradle 文件的 defaultConfig 代码块内配置另一个 externalNativeBuild 代码块,为 CMake 或 ndk-build 指定可选参数和标志。与 defaultConfig 代码块中的其他属性类似,您可以在构建配置中为每个产品风味 (product flavor) 覆盖这些属性。

例如,如果您的 CMake 或 ndk-build 项目定义了多个原生库和可执行文件,您可以使用 targets 属性仅为给定的产品风味构建和打包这些工件的子集。以下代码示例描述了您可以配置的一些属性。

Groovy

android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake or ndk-build script.
    externalNativeBuild {

      // For ndk-build, instead use the ndkBuild block.
      cmake {

        // Passes optional arguments to CMake.
        arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"

        // Sets a flag to enable format macro constants for the C compiler.
        cFlags "-D__STDC_FORMAT_MACROS"

        // Sets optional flags for the C++ compiler.
        cppFlags "-fexceptions", "-frtti"
      }
    }
  }

  buildTypes {...}

  productFlavors {
    ...
    demo {
      ...
      externalNativeBuild {
        cmake {
          ...
          // Specifies which native libraries or executables to build and package
          // for this product flavor. The following tells Gradle to build only the
          // "native-lib-demo" and "my-executible-demo" outputs from the linked
          // CMake project. If you don't configure this property, Gradle builds all
          // executables and shared object libraries that you define in your CMake
          // (or ndk-build) project. However, by default, Gradle packages only the
          // shared libraries in your app.
          targets "native-lib-demo",
                  // You need to specify this executable and its sources in your CMakeLists.txt
                  // using the add_executable() command. However, building executables from your
                  // native sources is optional, and building native libraries to package into
                  // your app satisfies most project requirements.
                  "my-executible-demo"
        }
      }
    }

    paid {
      ...
      externalNativeBuild {
        cmake {
          ...
          targets "native-lib-paid",
                  "my-executible-paid"
        }
      }
    }
  }

  // Use this block to link Gradle to your CMake or ndk-build script.
  externalNativeBuild {
    cmake {...}
    // or ndkBuild {...}
  }
}

Kotlin

android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake or ndk-build script.
    externalNativeBuild {

      // For ndk-build, instead use the ndkBuild block.
      cmake {

        // Passes optional arguments to CMake.
        arguments += listOf("-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang")

        // Sets a flag to enable format macro constants for the C compiler.
        cFlags += listOf("-D__STDC_FORMAT_MACROS")

        // Sets optional flags for the C++ compiler.
        cppFlags += listOf("-fexceptions", "-frtti")
      }
    }
  }

  buildTypes {...}

  productFlavors {
    ...
    create("demo") {
      ...
      externalNativeBuild {
        cmake {
          ...
          // Specifies which native libraries or executables to build and package
          // for this product flavor. The following tells Gradle to build only the
          // "native-lib-demo" and "my-executible-demo" outputs from the linked
          // CMake project. If you don't configure this property, Gradle builds all
          // executables and shared object libraries that you define in your CMake
          // (or ndk-build) project. However, by default, Gradle packages only the
          // shared libraries in your app.
          targets += listOf("native-lib-demo",
                  // You need to specify this executable and its sources in your CMakeLists.txt
                  // using the add_executable() command. However, building executables from your
                  // native sources is optional, and building native libraries to package into
                  // your app satisfies most project requirements.
                  "my-executible-demo")
        }
      }
    }

    create("paid") {
      ...
      externalNativeBuild {
        cmake {
          ...
          targets += listOf("native-lib-paid",
                  "my-executible-paid")
        }
      }
    }
  }

  // Use this block to link Gradle to your CMake or ndk-build script.
  externalNativeBuild {
    cmake {...}
    // or ndkBuild {...}
  }
}

要详细了解如何配置产品风味和构建变体,请访问配置构建变体。有关可使用 arguments 属性为 CMake 配置的变量列表,请参阅使用 CMake 变量

包含预构建的原生库

如果您希望 Gradle 打包未在任何外部原生构建中使用过的预构建原生库,请将它们添加到模块的 src/main/jniLibs/ABI 目录中。

4.0 之前的 Android Gradle 插件版本要求在 jniLibs 目录中包含 CMake IMPORTED 目标,以便将它们包含在应用中。如果您是从旧版本插件迁移而来,可能会遇到如下错误:

* What went wrong:
Execution failed for task ':app:mergeDebugNativeLibs'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
   > More than one file was found with OS independent path 'lib/x86/libprebuilt.so'

如果您使用的是 Android Gradle 插件 4.0,请将 IMPORTED CMake 目标使用的任何库移出 jniLibs 目录,以避免此错误。

指定 ABI

默认情况下,Gradle 会为 NDK 支持的应用程序二进制接口 (ABI) 将您的原生库构建为单独的 .so 文件,并将它们全部打包到您的应用中。如果您希望 Gradle 仅构建和打包原生库的特定 ABI 配置,可以使用模块级 build.gradle 文件中的 ndk.abiFilters 标志来指定它们,如下所示:

Groovy

android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      cmake {...}
      // or ndkBuild {...}
    }

    // Similar to other properties in the defaultConfig block,
    // you can configure the ndk block for each product flavor
    // in your build configuration.
    ndk {
      // Specifies the ABI configurations of your native
      // libraries Gradle should build and package with your app.
      abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
                   'arm64-v8a'
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

Kotlin

android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      cmake {...}
      // or ndkBuild {...}
    }

    // Similar to other properties in the defaultConfig block,
    // you can configure the ndk block for each product flavor
    // in your build configuration.
    ndk {
      // Specifies the ABI configurations of your native
      // libraries Gradle should build and package with your app.
      abiFilters += listOf("x86", "x86_64", "armeabi", "armeabi-v7a",
                   "arm64-v8a")
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

在大多数情况下,您只需在 ndk 代码块中指定 abiFilters(如上所示),因为它会告诉 Gradle 同时构建和打包这些版本的原生库。但是,如果您想独立于打包内容来控制 Gradle 的构建内容,可以在 defaultConfig.externalNativeBuild.cmake 代码块(或 defaultConfig.externalNativeBuild.ndkBuild 代码块)中配置另一个 abiFilters 标志。Gradle 会构建这些 ABI 配置,但仅打包您在 defaultConfig.ndk 代码块中指定的那些配置。

建议使用 Android App Bundle 进行发布,以进一步减小应用体积,因为下载时只会随应用提供与用户设备 ABI 相匹配的原生库。

对于使用 APK 发布的老旧应用(2021 年 8 月之前创建),请考虑基于 ABI 配置多个 APK——Gradle 不会创建一个包含所有版本原生库的大型 APK,而是为每个要支持的 ABI 创建一个单独的 APK,并且只打包每个 ABI 所需的文件。如果您在配置每 ABI 多个 APK 时未按照上述代码示例指定 abiFilters 标志,Gradle 会构建您原生库的所有支持的 ABI 版本,但只打包您在多个 APK 配置中指定的那些版本。为避免构建不需要的原生库版本,请为 abiFilters 标志和每 ABI 多个 APK 配置提供相同的 ABI 列表。