Android Studio 如何关联 Gradle

前面的小节我们学习了如何配置 CMake。本小节学习如何把原生库和 Gradle 构建关联起来。

1. 概述

如需添加我们的原生库项目作为 Gradle 构建依赖项,我们需要向 Gradle 提供 CMake 或 ndk-build 脚本文件的路径。当我们构建应用时,Gradle 会运行 CMake 或 ndk-build,并将共享的库打包到我们的 APK 中。

Gradle 还会使用构建脚本来了解要将哪些文件添加到我们的 Android Studio 项目中,以便我们可以从 Project 窗口访问这些文件。如果我们没有原生源代码文件的构建脚本,则需要先创建 CMake 构建脚本,然后再继续。

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

2. 通过界面配置

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

  1. 从 IDE 左侧打开 Project 窗格,然后选择 Android 视图。

  2. 右键点击我们想要关联到原生库的模块(例如 app 模块),然后从菜单中选择 Link C++ Project with Gradle

  3. 从下拉菜单中,选择 CMakendk-build

    • 如果选择 CMake,请使用 Project Path 旁的字段为我们的外部 CMake 项目指定 CMakeLists.txt 脚本文件。

    • 如果选择 ndk-build,请使用 Project Path 旁的字段为我们的外部 ndk-build 项目指定 Android.mk 脚本文件。如果 Application.mk 文件与我们的 Android.mk 文件位于同一目录下,Android Studio 也会包含此文件。

  4. 点击 OK

3. 通过手动配置

要手动配置 Gradle 以关联到我们的原生库,我们需要将 externalNativeBuild 块添加到模块级 build.gradle 文件中,并使用 cmakendkBuild 块对其进行配置:

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"
    }
  }
}

3.1 指定可选配置

我们可以在模块级 build.gradle 文件的 defaultConfig 块中配置另一个 externalNativeBuild 块,为 CMake 或 ndk-build 指定可选参数和标记。与 defaultConfig 块中的其他属性类似,我们也可以在构建配置中为每种产品特性重写这些属性。

例如,如果我们的 CMake 或 ndk-build 项目定义多个原生库和可执行文件,我们可以使用 targets 属性为指定产品特性构建和打包其中的部分工件。以下代码示例说明了我们可以配置的部分属性:

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 APK.
          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 APK 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 {...}
  }
}

3.2 添加预构建的原生库

如果希望 Gradle 将预构建的原生库打包到我们的 APK 中,请修改默认的源代码集配置,以添加预构建 .so 文件所在的目录,如下所示。请注意,若要添加关联到 Gradle 的 CMake 构建脚本的工件,则无需执行此操作。

android {
    ...
    sourceSets {
        main {
            jniLibs.srcDirs 'imported-lib/src/', 'more-imported-libs/src/'
        }
    }
}

3.3 指定 ABI

默认情况下,Gradle 会针对 NDK 支持的应用二进制接口 (ABI) 将我们的原生库构建到单独的 .so 文件中,并将这些文件全部打包到我们的 APK 中。如果希望 Gradle 仅构建和打包原生库的部分 ABI 配置,则可以在模块级 build.gradle 文件中使用 ndk.abiFilters 标记指定这些配置,如下所示:

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 APK.
      abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
                   'arm64-v8a'
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

在大多数情况下,我们只需要在 ndk 块中指定 abiFilters,因为它会指示 Gradle 构建和打包原生库的这些版本。但是,如果我们想控制 Gradle 应当构建的配置,而不依赖于我们希望其打包到 APK 中的配置,请在 defaultConfig.externalNativeBuild.cmake 块(或 defaultConfig.externalNativeBuild.ndkBuild 块)中配置另一个 abiFilters 标记。Gradle 会构建这些 ABI 配置,但只会打包我们在 defaultConfig.ndk 块中指定的配置。

4. 小结

本节课程我们主要学习了如何把原生库和 Gradle 构建关联起来。本节课程的重点如下:

  • 掌握如何添加 Gradle 关联配置。