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

分類:IT技術 時間:2016-10-18

使用 android studio,你可以將 C 和 C++ 代碼編譯成 native library,然後打包到你的 APK 中。你的 Java 代碼可以通過 Java Native interface(JNI)調用 native library 庫中的方法。如果你想了解更多關於如何使用 JNI,請參閱 JNI tips for Android。

Android Studio 默認使用 CMake 編譯原生庫。由於已經有大量的代碼使用了 ndk-build 來編譯 native code,所以 Android Studio 同樣也支持 ndk build。如果你想導入一個 ndk-build 庫到你的 Android Studio 項目中,請參閱 link to your native library 。然而,如果你創建了一個新的 native 庫工程,你應該使用 CMake。

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

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

下載 NDK 和構建工具

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

  • The Android Native Development Kit (NDK) : 讓你能在 Android 上面使用 C 和 C++ 代碼的工具集。

  • `CMake `: 外部構建工具。如果你準備只使用 ndk-build 的話,可以不使用它。

  • LLDB : Android Studio 上面調試本地代碼的工匠。

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

  1. 打開一個項目,從菜單欄中選擇 Tools > Android > SDK Manager

  2. 點擊 SDK Tools 選項卡。

  3. 勾選 LLDB,CMakeNDK 。如圖一:

  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 會添加 cppExternal 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 腳本文件的路徑。

  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 中。

 

來自:http://wl9739.github.io/2016/09/21/在-Android-Studio-2-2-中愉快地使用-C-C-md/

 


Tags: Android library 菜單欄 Java 文章

文章來源:


ads
ads

相關文章
ads

相關文章

ad