天天看點

Android NDK 編譯工具CMake的使用

Android NDK 編譯工具CMake的使用

    • 1. 建立新 Android 項目
    • 2.自定義C++的配置
    • 3.分析項目結構
      • 3.1 so庫/native方法
      • 3.2 原生源檔案
      • 3.3 建構配置
      • 3.4 腳本配置
      • 3.5 運作流程
    • 參考資料
    • Author
Android Studio 用于建構原生庫的預設工具是 CMake。由于很多現有項目都使用建構工具包編譯其原生代碼,Android Studio 還支援 ndk-build。不過,如果您在建立新的原生庫,則應使用 CMake。

建立支援原生代碼的項目與建立任何其他 Android Studio 項目類似,不過前者還需要額外幾個步驟:

1. 建立新 Android 項目

建立新的 Android 項目,需要選中 Include C++ Support 複選框,即支援C++的。

Android NDK 編譯工具CMake的使用

2.自定義C++的配置

依次

Next

後,

Create Android project

>

Target Android Devices

>

Add an Activity to Mobile

>

Configure Activity

>

Customize C++ Support

Android NDK 編譯工具CMake的使用

為了友善分析,Exceptions Support、Runtime Type Information Support 我們也同時勾選上,不過一般情況下可以不勾選。

Android NDK 編譯工具CMake的使用

在 Customize C++ Support中,我們可以自定義一下内容:

  • C++ Standard :使用下拉清單選擇你希望使用哪種 C++ 标準。選擇 Toolchain Default 會使用預設的 CMake 設定。
  • Exceptions Support :如果你希望啟用對 C++ 異常處理的支援,請選中此複選框。如果啟用此複選框,Android Studio 會将

    -fexceptions

    标志添加到子產品級 build.gradle 檔案的 cppFlags 中,Gradle 會将其傳遞到 CMake。
  • Runtime Type Information Support :如果你希望支援 RTTI,請選中此複選框。如果啟用此複選框,Android Studio 會将

    -frtti

    标志添加到子產品級 build.gradle 檔案的 cppFlags 中,Gradle 會将其傳遞到 CMake。

3.分析項目結構

先切換視圖到

Project

友善檢視。

Android NDK 編譯工具CMake的使用
Android NDK 編譯工具CMake的使用

對比普通建立項目的内容,可以發現示例有4個地方發生了變化:

  1. 添加了

    cpp

    目錄,裡面有示例 C++ 源檔案

    native-lib.cpp

    ,它的代碼裡面提供了一個簡單的 C++ 函數 stringFromJNI(),此函數可以傳回字元串“Hello from C++”。
  2. MainActivity.java

    中添加了兩個部分,一個負責加載so庫;一個則為native方法。
  3. build.gradle

    中也添加了兩個部分,一個為C++添加異常處理、RTTI的處理的支援;一個則為CMake封裝建構配置,同時也提供一個相對路徑給CMake的建構腳本

    CMakeLists.txt

    ;
  4. 添加了

    CMakeLists.txt

    腳本檔案,它的主要作用是定義了哪些檔案需要編譯以及和其他庫的關系,即輔助Cmake進行系列的操作。

注:native相關方法去掉報紅

取消檢測即可,打開 Settings>Editor>Inspections>Android>Missing JNI function 去掉勾選。

Android NDK 編譯工具CMake的使用

新的項目完成建立後,我們可以清晰地看到結構及内容上的變化:

3.1 so庫/native方法

MainActivity.java

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
}
           

下面說明一下相關的内容:

  • System.loadLibrary(“native-lib”)負責加載動态的原生庫檔案,

    native-lib

    CMakeLists.txt

    中定義的library。

CMake 使用以下規範來為庫檔案命名:

格式:

lib庫名稱.so

注:如果你在建構腳本中指定“native-lib”作為共享庫的名稱,CMake 将建立一個名稱為

libnative-lib.so

的檔案。不過,在 Java 代碼中加載此庫時,請使用你在 CMake 建構腳本中指定的名稱。

  • stringFromJNI為native方法與

    native-lib.cpp

    的Java_com_brainbg_nkdcmakeall_MainActivity_stringFromJNI的相對應的。

3.2 原生源檔案

native-lib.cpp

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_brainbg_nkdcmakeall_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
           

其中

Java_com_brainbg_nkdcmakeall_MainActivity_stringFromJNI

是根據

MainActivity.java

stringFromJNI()

生成的,其格式如下:

格式:Java_包名_類名_方法名

3.3 建構配置

這部分主要是關于Cmake、C++相關的建構配置。

build.gradle

android {
    defaultConfig {
    ......
    //Exceptions Support(-fexceptions)、Runtime Type Information (-frtti) 建構關聯
        externalNativeBuild {
            cmake {
                cppFlags "-frtti -fexceptions"
            }
        }
    }

    //封裝CMake建構配置
    externalNativeBuild {
         //提供一個相對路徑給CMake建構腳本
        cmake {
            path "CMakeLists.txt"
        }
    }
}
           

對比ndk-build和CMake的配置

//CMake需要關聯CMakeLists.txt
 externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }


//ndk-build需要關聯Android.mk
 externalNativeBuild {
        ndkBuild {
            path file('jni/Android.mk')
        }
    }

           

3.4 腳本配置

CMakeLists.txt

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets 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 )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

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

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
           

上面的配置簡單說明下:

  • cmake_minimum_required:CMake所需的最低版本要求,目前為3.4.1。
  • add_library:
    • Sets the name of the library:設定庫的名稱。例如指定

      native-lib

      作為共享庫的名稱,那麼CMake 将建立一個名稱為

      libnative-lib.so

      的庫檔案。
    • Sets the library as a shared library:将庫設定為共享庫.
    • Provides a relative path to your source file(s):提供源檔案的相對路徑
  • find_library:
  • target_link_libraries:

更多關于Cmake的用法可以檢視CMake文檔

3.5 運作流程

下面為官方NDK文檔的流程說明:

  1. Gradle

    調用您的外部建構腳本

    CMakeLists.txt

  2. CMake

    按照建構腳本中的指令将 C++ 源檔案

    native-lib.cpp

    編譯到共享的對象庫中,并命名為

    libnative-lib.so

    ,Gradle 随後會将其打包到 APK 中。
  3. 運作時,應用的

    MainActivity

    會使用

    System.loadLibrary()

    加載原生庫。現在,應用可以使用庫的原生函數

    stringFromJNI()

  4. MainActivity.onCreate()

    調用

    stringFromJNI()

    ,這将傳回“Hello from C++”并使用這些文字更新

    TextView

根據本人的了解,制作了一張簡單流程圖:

Android NDK 編譯工具CMake的使用

參考資料

https://github.com/googlesamples/android-ndk/tree/master/hello-libs

https://github.com/android-ndk/ndk

https://developer.android.google.cn/studio/projects/add-native-code.html#link-gradle

https://cmake.org/cmake/help/latest/index.html#

Author

作者:Brainbg(白雨)

GitHub:https://github.com/Brainbg

部落格:https://www.brainbg.com/

CSDN:https://blog.csdn.net/u014720022

簡書:https://www.jianshu.com/u/94518ede7100