將 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 指令碼檔案中包含其他 Makefiles

將 Gradle 連結至原生專案後,Android Studio 會更新「Project」(專案) 窗格,在「cpp」群組中顯示源檔案和原生資料庫,以及在「External Build Files」(外部建構檔案) 群組中顯示外部建構指令碼。

注意:在變更 Gradle 設定時,請務必按一下工具列中的「Sync Project」(同步處理專案) 以套用變更。此外,在將 CMake 或 ndk-build 構建指令碼連接至 Gradle 後對其進行變更時,應從選單列中依序選取「Build」(構建) >「Refresh Linked C++ Projects」(重新整理連接的 C++ 專案),在 Android Studio 同步您的變更。

您可以使用 Android Studio UI 將 Gradle 連結至外部 CMake 或 ndk-build 專案:

  1. 開啟 IDE 左側的「Project」(專案) 窗格,然後選取「Android」檢視畫面。
  2. 在要連結至原生資料庫的模組 (例如「app」(應用程式) 模組) 上按一下滑鼠右鍵,然後從選單選取「Link C++ Project with Gradle」(連結 C++ 專案與 Gradle)。系統會顯示類似圖 4 的對話方塊。
  3. 在下拉式選單中選取「CMake」或「ndk-build」
    1. 如果您選取「CMake」,請使用「Project Path」(專案路徑) 旁邊的欄位為您的外部 CMake 專案指定 CMakeLists.txt 指令碼檔案。
    2. 如果您選取「ndk-build」,請使用「Project Path」(專案路徑) 旁邊的欄位為您的外部 ndk-build 專案指定 Android.mk 指令碼檔案。如果檔案位於 Android.mk 檔案所在的目錄中,Android Studio 也會包含 Application.mk 檔案。

    圖 4. 使用 Android Studio 對話方塊連結外部 C++ 專案。

  4. 按一下 確定

手動設定 Gradle

如要手動設定 Gradle 以連接至原生資料庫,您必須在模組層級 build.gradle 檔案中新增 externalNativeBuild 區塊,並使用 cmakendkBuild 區塊進行設定:

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 檔案的相對路徑。如果檔案位於 Android.mk 檔案所在的目錄中,Gradle 也會包含 Application.mk 檔案。

指定選用設定

您可以為 CMake 或 ndk-build 指定選用引數和旗標,透過在模組層級 build.gradle 檔案的 defaultConfig 區塊中設定另一個 externalNativeBuild 區塊。與 defaultConfig 區塊中的其他屬性類似,您可以在建構設定中為每項變種版本覆寫這些屬性。

例如,如果您的 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 Plugin 4.0,請將 IMPORTED CMake 目標使用的所有資料庫從 jniLibs 目錄移出,以避免此錯誤。

指定 ABI

根據預設,Gradle 會將原生資料庫建構為獨立 .so 檔案,以便用於NDK 支援的應用程式二進位檔介面 (ABI),並將這些檔案封裝至應用程式中。如果您只想讓 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,而不是使用所有版本的原生資料庫建立一個大型 APK,Gradle 會為每個要支援的 ABI 建立單獨的 APK,而且只會封裝每個 ABI 所需的檔案。如果您為每個 ABI 設定多個 APK,但未指定上方程式碼範例的 abiFilters 旗標,Gradle 會建構所有支援 ABI 版本的原生資料庫,並且只會封裝您在多個 APK 設定中指定的檔案。為避免建立您不想要的原生資料庫版本,請為 abiFilters 旗標和每個 ABI 的多重 APK 設定提供相同的 ABI 清單。