在 Android Studio 2.2 中愉快地使用 C/C++

 Android Studio 2.2 正式版發佈後,看到更新內容中有提到對 C/C++ 支持的完善,表示很是高興。而後將官網上這一部份內容翻譯出來,若有錯誤,歡迎指正。html

原文連接:Add C and C++ Code to Your Projectjava

使用 Android studio,你能夠將 C 和 C++ 代碼構建成 native library(即 .so 文件),而後打包到你的 APK 中。你的 Java 代碼能夠經過 Java Native Interface(JNI)調用 native library 庫中的方法。android

Android Studio 默認使用 CMake 編譯原生庫。因爲已經有大量的代碼使用了 ndk-build 來編譯 native code,因此 Android Studio 一樣也支持 ndk build。若是你想導入一個 ndk-build 庫到你的 Android Studio 項目中,請參閱後文的 關聯本地庫與 Gradle 部分。然而,若是你建立了一個新的 native 庫工程,你應該使用 CMake。bash

本篇文章將會說明如何使用 Android Studio 來建立、配置 Android 項目,以支持 native code,以及將其運行到你的 app 中。架構

注意:要在 Android Studio 中使用 CMake 或者 ndk-build,你須要使用 Android Studio 2.2 或更高的版本,同時須要配合使用 Android Plugin for Gradle 2.2.0 及以上的版本。app

下載 NDK 和構建工具

要編譯和調試本地代碼(native code),你須要下面的組件:框架

  • The Android Native Development Kit (NDK): 讓你能在 Android 上面使用 C 和 C++ 代碼的工具集。
  • CMake: 外部構建工具。若是你準備只使用 ndk-build 的話,能夠不使用它。
  • LLDB: Android Studio 上面調試本地代碼的工具。

你可使用 SDK Manager 來安裝上述組件:ide

  1. 打開一個項目,從菜單欄中選擇 Tools > Android > SDK Manager
  2. 點擊 SDK Tools 選項卡。
  3. 勾選 LLDB,CMake 和 NDK。如圖一:
  1. 點擊 Apply,而後點擊 OK
  2. 當安裝完成後,點擊 Finish,而後點擊 OK

 建立支持 C/C++ 的新項目

建立一個支持 native code 的項目和建立普通的 Android studio 工程很像。可是有幾點須要留意的地方:函數

  1. 在 Configure your new project 選項中,勾選 Include C++ Support 選項。
  2. 點擊 Next,後面的流程和建立普通的 Android studio 工程同樣。
  3. 在 Customize C++ Support 選項卡中。你有下面幾種方式來自定義你的項目:工具

    • C++ Standard:點擊下拉框,能夠選擇標準 C++,或者選擇默認 CMake 設置的 Toolchain Default 選項。
    • Exceptions Support:若是你想使用有關 C++ 異常處理的支持,就勾選它。勾選以後,Android Studio 會在 module 層的 build.gradle 文件中的 cppFlags 中添加 -fexcetions 標誌。
    • Runtime Type Information Support:若是你想支持 RTTI,那麼就勾選它。勾選以後,Android Studio 會在 module 層的 build.gradle 文件中的 cppFlags 中添加 -frtti 標誌。
  4. 點擊 「Finish」。

當 Android Studio 完成新項目建立後,打開 Project 面板,選擇 Android 視圖。Android Studio 會添加 cpp 和 External Build Files 目錄。

  1. cpp 目錄存放你全部 native code 的地方,包括源碼,頭文件,預編譯項目等。對於新項目,Android Studio 建立了一個 C++ 模板文件:native-lib.cpp,而且將該文件放到了你的 app 模塊的 src/main/cpp/ 目錄下。這份模板代碼提供了一個簡答的 C++ 函數:stringFromJNI(),該函數返回一個字符串:」Hello from C++」。
  2. External Build Files 目錄是存放 CMake 或 ndk-build 構建腳本的地方。有點相似於 build.gradle 文件告訴 Gradle 如何編譯你的 APP 同樣,CMake 和 ndk-build 也須要一個腳原本告知如何編譯你的 native library。對於一個新的項目,Android Studio 建立了一個 CMake 腳本:CMakeLists.txt,而且將其放到了你的 module 的根目錄下。

編譯運行示例 APP

當你點擊 Run 按鈕,Android Studio 會編譯並啓動一個 APP ,而後在 APP 中顯示一段文字」Hello from C++」。從編譯到運行示例 APP 的流程簡單概括以下:

  1. Gradle 調用外部構建腳本,也就是 CMakeLists.txt
  2. CMake 會根據構建腳本的指令去編譯一個 C++ 源文件,也就是 native-lib.cpp,並將編譯後的產物扔進共享對象庫中,並將其命名爲 libnative-lib.so,而後 Gradle 將其打包到 APK 中。
  3. 在運行期間,APP 的 MainActivity 會調用 System.loadLibrary() 方法,加載 native library。而這個庫的原生函數,stringFromJNI(),就能夠爲 APP 所用了。
  4. MainActivity.onCreate() 方法會調用 stringFromJNI(),而後返回 「Hello from C++」,並更新 TextView 的顯示。

注意:Instant Run 並不兼容使用了 native code 的項目。Android Studio 會自動禁止 Instant Run 功能。

若是你想驗證一下 Gradle 是否將 native library 打包進了 APK,你可使用 APK Analyzer:

  1. 選擇 Build > Analyze APK
  2. 從 app/build/outputs/apk/ 路徑中選擇 APK,並點擊 OK
  3. 以下圖,在 APK Analyzer 窗口中,選擇 lib/<ABI>/,你就能夠看見 libnative-lib.so 。

 

將 C/C++ 代碼添加到現有的項目中

若是你想將 native code 添加到一個現有的項目中,請按照下面的步驟操做:

  1. 建立新的 native source 文件,並將其添加到你的 Android Studio 項目中。若是你已經有了 native code,也能夠跳過這一步。
  2. 建立一個 CMake 構建腳本。若是你已經有了一個 CMakeLists.txt 構建腳本,或者你想使用 ndk-build 而後有一個 Android.mk 構建腳本,也能夠跳過這一步。
  3. 將你的 native library 與 Gradle 關聯起來。Gradle 使用構建腳本將源碼導入到你的 Android Studio 項目中,而且將你的 native library (也就是 .so 文件)打包到 APK 中。

一旦你配置好了項目,你就能夠在 Java 代碼中,使用 JNI 框架開調用原生函數(native functions)。只須要點擊 Run 按鈕,就能夠編譯運行你的 APP 了。

建立新的 native source 文件

請按照下面的方法來建立一個 cpp/ 目錄和源文件(native source files):

  1. 打開IDE左邊的 Project 面板,選擇 Project 視圖。
  2. 找到你項目的 module > src 目錄,右鍵點擊 main 目錄,選擇 New > Directory
  3. 輸入目錄的名字(好比 cpp),而後點擊 OK
  4. 右鍵點擊剛纔建立好的目錄,選擇 New > C/C++ Source File
  5. 輸入文件名,好比 native-lib
  6. 在 Type 菜單下拉選項中,選擇源文件的擴展後綴名,好比 .cpp
  7. 若是你也想建立一個頭文件,點擊 Create an associated header 選項框。
  8. 點擊 OK

 建立 CMake 構建腳本

若是沒有一個 CMake 構建腳本,你須要本身手動建立一個,並添加一些合適的 CMake 命令。CMake 構建腳本是一個空白的文本文檔(後綴爲 .txt 的文件),名字必須爲 CMakeLists.txt

注意:若是你的項目使用了 ndk-build,你就不須要建立 CMake 構建腳本,只須要提供一個路徑鏈,將你的 Android.mk 文件連接到 Gradle 中便可。

將一個空白的文本文檔變成一個 CMake 構建腳本,你須要這麼作:

  1. 打開 IDE 左邊的 Project 面板,選擇 Project 視圖。
  2. 在你的 module 根目錄下,右鍵,選擇 New > File
  3. 輸入 「CMakeLists.txt」 做爲文件名,並點擊 OK

如今,你能夠添加 CMake 命令來配置你的構建腳本了。爲了讓 CMake 將源代碼(native source code)編譯成 native library。須要在編譯文件中添加 cmake_minimum_required() 和 add_library() 命令:

# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.

cmake_minimum_required(VERSION 3.4.1)

# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add.library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.

add_library( # Specifies the name of the library.
             native-lib

            # Sets the library as a shared library.
             SHARED

            # Provides a relative path to your source file(s).
            src/main/cpp/native-lib.cpp )

當你使用 add_library(),將一個源文件(source file)或庫添加到你的 CMake 構建腳本,同步你的項目,而後你會發現 Android studio 將關聯的頭文件也顯示了處理。然而,爲了讓 CMake 在編譯時期能定位到你的頭文件,你須要在 CMake 構建腳本中添加 include_directories() 命令,並指定頭文件路徑:

add_library(...)

# Specifies a path to native header files.
include_directories(src/main/cpp/include/)

而後,按照約定,CMake 會將生成的 library 命名爲下面的形式:

lib*library-name*.so

好比,若是你在構建腳本中,將 library 命名爲 「native-lib」,那麼 CMake 會建立叫 libnative-lib.so 的文件。可是,當你將 library 加載到 Java 代碼中的時候, 你須要使用在 CMake 中指定的名稱:

static {
        System.loadLibrary(「native-lib」);
}

注意:若是你將 CMake 腳本里面的 library 重命名了,或者移除了。你須要清理一下你的工程。在 IDE 的菜單欄中選擇 Build > Clean Project

Android Studio 會在 Project 面板中的 cpp 目錄中自動添加源文件和頭文件。你能夠屢次使用 add_library() 命令,來添加額外的 library。

添加 NDK APIs

Android NDK 提供了一些有用的 native APIs。將 NDK librarys 添加到 CMakeLists.txt 腳本文件中,就可使用這些 API 了。

預編譯的 NDK librarys 已經存在在 Android 平臺中了,因此你不須要編譯它們,或者是將其打包到你的 APK 中。由於這些 NDK librarys 已是 CMake 搜索路徑的一部分,你甚至不須要提供你本地安裝的 NDK 路徑。你只須要向 CMake 提供你想使用的 library 名字。

將 find_library() 命令添加到你的 CMake 構建腳本中,這樣就能夠定位 NDK library 的位置,並將其位置存儲在一個變量之中。你能夠在構建腳本的其餘地方使用這個變量,來代指 NDK library。下面的示例代碼將 Android-specific log support library 的位置存儲到變量 log-lib 中:

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

              # Specifies the name of the NDK library that
              # CMake needs to locate.
              log )

NDK 一樣也包含一些只包含源碼的 library,這些就須要你去編譯,而後連接到你的本地庫(native library)。你能夠在 CMake 構建腳本中使用 add_library() 命令將源碼編譯進本地庫。這時就須要提供你的本地 NDK 安裝路徑,一般將該路徑保存在 ANDROID_NDK 變量中,這樣 Android Studio 能夠自動爲你識別。

下面的命令告訴 CMake 去構建 android_native_app_glue.c,這個命令能夠管理 NativeActivity 的生命週期以及點擊輸入,並將其導入靜態庫中,而後將其連接至 native-lib

add_library( app-glue
             STATIC
             ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )

# You need to link static libraries against your shared native library.
target_link_libraries( native-lib app-glue ${log-lib} )

添加其餘的預編譯庫

添加預編譯庫和添加本地庫(native library)相似。因爲預編譯庫是已經構建好的,你想就要使用 IMPORTED 標誌去告訴 CMake ,你只須要將其導入到你的項目中便可:

add_library( imported-lib
             SHARED
             IMPORTED )

而後你須要使用 set_target_properties() 命令去指定庫的路徑,就像下面的代碼那樣。

一些庫會根據不一樣的 CPU 使用不一樣的包,或者是 Application Binary Interfaces(ABI),而且將他們歸類到不一樣的目錄中。這樣作的好處是,能夠充分發揮特定的 CPU 架構。你可使用 ANDROID_ABI 路徑變量,將多個 ABI 版本的庫添加到你的 CMake 構建腳本中。這個變量使用了一些 NDK 默認支持的 ABI,以及一些須要手動配置到 Gradle 的 ABI,好比:

add_library(...)
set_target_properties( # Specifies the target library.
                       imported-lib

                       # Specifies the parameter you want to define.
                       PROPERTIES IMPORTED_LOCATION

                       # Provides the path to the library you want to import.
                       imported-lib/src/${ANDROID_ABI}/libimported-lib.so )

爲了讓 CMake 在編譯時期能找到你的頭文件,你須要使用 include_directories() 命令,而且將你的頭文件地址傳進去:

include_directories( imported-lib/include/ )

在 CMake 構建腳本中使用 target_link_libraries() 命令,將預構建庫與你本地庫相關聯:

target_link_libraries( native-lib imported-lib app-glue ${log-lib} )

當你構建你的 APP 的時候,Gradle 會自動將導入的庫打包到你的 APK 中。你可使用 APK Analyzer 來檢查。

關聯本地庫與 Gradle

爲了將本地庫與 Gradle 相關聯,你須要在 CMake 或 ndk-build 構建腳本中提供一個路徑地址。當你構建你的 APP 時,Gradle 會將 CMake 或 ndk-build 做爲一個依賴運行,而後將共享庫(.so 文件)打包到你的 APK 中。Gradle 一樣使用構建腳原本識別哪些文件須要導入到 Android Studio 項目,你能夠從 Project 窗口面板中看到相應的文件。若是你尚未一個爲 native sources 準備的構建腳本,你須要先建立一個。

使用 Android Studio 圖形化界面

你可使用 Android Studio 的圖形化界面來將 Gradle 與外部 CMake 或者 ndk-build 項目關聯起來。

  1. 打開 IDE 左邊的 Project 面板,選擇 Android 視圖。
  2. 右鍵點擊你想連接本地庫的 module,好比 app module,而後從菜單中選擇 Link C++ Project with Gradle。你應該能看見一個和下圖很像的對話框。
  3. 在下拉菜單中,選擇 CMake 或者 ndk-build。 
    a. 若是你選擇 CMake,須要在 Project Path 中指定 CMakeLists.txt 腳本文件的路徑。 
    b. 若是你選擇 ndk-build,你須要在 Project Path 中指定 Android.mk 腳本文件的路徑。

    Figure 3.jpg-86.4kB

  4. 點擊 OK

 

手動配置 Gradle

若是要手動將 Gradle 與你的本地庫相關聯,你須要在 module 層級的 build.gradle 文件中添加 externalNativeBuild {} 代碼塊,而且在該代碼塊中配置 cmake {} 或 ndkBuild {}

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

可選配置

你能夠在你的 module 層級的 build.gradle 文件中的 defaultConfig {} 代碼塊中,添加 externalNativeBuild {} 代碼塊,爲 CMake 或 ndk-build 配置一些額外參數。固然,你也能夠在你的構建配置中的其餘每個生產渠道重寫這些屬性。

好比,若是你的 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 ndkBuild {}
      cmake {

        // Passes optional arguments to CMake.
        arguments "-DCMAKE_VERBOSE_MAKEFILE=TRUE"

        // Sets optional flags for the C compiler.
        cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2"

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

  buildTypes {...}

  productFlavors {
    ...
    demo {
      ...
      externalNativeBuild {
        cmake {
          ...
          // Specifies which native libraries to build and package for this
          // product flavor. If you don't configure this property, Gradle
          // builds and packages all shared object libraries that you define
          // in your CMake or ndk-build project.
          targets "native-lib-demo"
        }
      }
    }

    paid {
      ...
      externalNativeBuild {
        cmake {
          ...
          targets "native-lib-paid"
        }
      }
    }
  }

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

指定 ABI

通常狀況下,Gradle 會將你的本地庫構建成 .so 文件,而後將其打包到你的 APK 中。若是你想 Gradle 構建並打包某個特定的 ABI 。你能夠在你的 module 層級的 build.gradle 文件中使用 ndk.abiFilters 標籤來指定他們:

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

    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 構建、依賴你但願的東西,你就須要在 defaultConfig.externalNativeBuild.cmake {} 代碼塊或 defaultConfig.externalNativeBuild.ndkBuild {} 代碼塊中,配置其餘的 abiFilters 標籤。Gradle 會構建這些 ABI 配置,可是隻會將 defaultConfig.ndk {} 代碼塊中指定的東西打包到 APk 中。

相關文章
相關標籤/搜索