天天看點

android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇

上一篇部落格,分享了 android studio ndk-build 編譯C生成.so檔案

這一篇文章和大家分享一下,用cMake腳本檔案添加ndk,來建構C/C++程式。

一、概要

Android Studio 用于建構原生庫的預設工具是 CMake,由于很多現有項目都使用建構工具包編譯其原生代碼,Android Studio 還支援 ndk-build。如果您想要将現有的 ndk-build 庫導入到您的 Android Studio 項目中,請參考上一篇文章:android studio ndk-build 編譯C生成.so檔案, 不過,如果您在建立新的原生庫,則應使用 CMake。

注意:至于ndk的現在和建構工具的配置:上一篇文章:android studio ndk-build 編譯C生成.so檔案 中已經介紹過了,這裡就不累贅了。

這篇部落客要是介紹兩部分内容:

  • 建立支援C/C++的新項目
  • 向現有的項目添加C/C++ 代碼

二、建立支援C/C++的新項目

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

  1. 在向導的 Configure your new project 部分,選中 Include C++ Support 複選框。
    android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇
  2. 點選 Next。
  3. 正常填寫所有其他字段并完成向導接下來的幾個部分。
  4. 在向導的 Customize C++ Support 部分,您可以使用下列選項自定義項目:
    android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇
    • C++ Standard:使用下拉清單選擇您希望使用哪種 C++ 标準。選擇 Toolchain Default 會使用預設的 CMake 設定。
    • Exceptions Support:如果您希望啟用對 C++ 異常處理的支援,請選中此複選框。如果啟用此複選框,Android Studio 會将 

      -fexceptions

       标志添加到子產品級 build.gradle 檔案的 cppFlags 中,Gradle 會将其傳遞到 CMake。</li><li><strong>Runtime Type Information Support</strong>:如果您希望支援 RTTI,請選中此複選框。如果啟用此複選框,Android Studio 會将 <code>-frtti 标志添加到子產品級 build.gradle 檔案的 cppFlags 中,Gradle 會将其傳遞到 CMake。
  5. 點選 Finish。

在 Android Studio 完成新項目的建立後,請從 IDE 左側打開 Project 窗格并選擇 Android 視圖。如圖 下圖 2-1 所示,Android Studio 将添加 cpp 和 External Build Files 組。Project 視圖如下:2-3所示。

android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇

圖 2-1

點選 Run 先運作項目看效果:如下圖 2-2 ,在 app/build/intrmediates/cmake/debug/obj下面可以看到libnative-lib.so檔案了,如下圖: 2-3

android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇

圖 2-2

android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇

圖: 2-3

我們再分析一下跟我們平時開發不帶ndk的項目的不同點

主要有四個改變:如下圖所示:

android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇

1,native-lib.cpp代碼如下:

#include <jni.h>
#include <string>
extern "C"
//注意函數名的命名方式: 1,包名 + 類名 + 方法名 2,'-'連接配接符
JNIEXPORT jstring JNICALL Java_com_niwoxuexi_cmakedemo_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
      

2,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} )
      

3, build.gradle 檔案中引入CMake腳本

externalNativeBuild {
    cmake {
        path "CMakeLists.txt"
    }
}
      

4,MainActivity中引入和使用so庫

public class MainActivity extends AppCompatActivity {
    // Used to load the 'native-lib' library on application startup.
    //加載so庫
    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);
        //調用native方法
        tv.setText(stringFromJNI());
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     *
     * 注意:java 調C中的方法都需要用native聲明且方法名必須和c的方法名一樣
     */
    public native String stringFromJNI();
}
      

下面的概覽介紹了建構和運作示例應用時會發生的事件:

  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。

說了這麼多,最好上代碼:項目下載下傳請點選我哦

三、向現有的項目添加C/C++代碼

如果你想向現有的項目添加原生代碼,請看下面的步驟

  1. 建立新的原生源檔案cpp檔案夾以及源檔案 并将其添加到您的 Android Studio 項目中。
  2. 建立 CMake 建構腳本,将您的原生源代碼建構到庫中。
  3. 提供一個指向您的 CMake ,将 Gradle 關聯到您的原生庫。Gradle 使用建構腳本将源代碼導入您的 Android Studio 項目并将原生庫(SO 檔案)打包到 APK 中。

配置完項目後,您可以使用 JNI 架構從 Java 代碼中通路您的原生函數。要建構和運作應用,隻需點選 Run 

android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇

。Gradle 會以依賴項的形式添加您的外部原生建構流程,用于編譯、建構原生庫并将其随 APK 一起打包。

1, 建立新的原生源檔案

要在應用子產品的主源代碼集中建立一個包含建立原生源檔案的 cpp 目錄,請按以下步驟操作:

  1. 從 IDE 的左側打開 Project 窗格并從下拉菜單中選擇 Project 視圖。
  2. 導航到 您的子產品 > src,右鍵點選 main 目錄,然後選擇 New > Directory。
  3. 為目錄輸入一個名稱(例如 

    cpp

    )并點選 OK。
  4. 右鍵點選您剛剛建立的目錄,然後選擇 New > C/C++ Source File。
  5. 為您的源檔案輸入一個名稱,例如 

    native-lib

  6. 從 Type 下拉菜單中,為您的源檔案選擇檔案擴充名,例如 

    .cpp

    • 點選 Edit File Types 
      android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇
      ,您可以向下拉菜單中添加其他檔案類型,例如 

      .cxx

       或 .hxx。在彈出的 C/C++ 對話框中,從 Source Extension 和 Header Extension 下拉菜單中選擇另一個檔案擴充名,然後點選 OK。
  7. 如果您還希望建立一個标頭檔案,請選中 Create an associated header 複選框。
  8. 點選 OK。

native-lib代碼如下(也可以通過ndk-build 自動生成頭檔案,再編寫,請參看上一篇文章: android studio ndk-build 編譯C生成.so檔案):

#include <jni.h>
#include <string>
extern "C"
//注意函數名的命名方式: 1,包名 + 類名 + 方法名 2,'-'連接配接符
JNIEXPORT jstring JNICALL Java_com_niwoxuexi_cmakedemo_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
      

2, 建立 CMake 建構腳本

CMake 建構腳本是一個純文字檔案,您必須将其命名為 

CMakeLists.txt

。 我把他放在項目app的根目錄,(或者其他的地方也可以,注意下面關聯Cmake腳本檔案的時候,選擇你所放置的路徑)

我們先上代碼:

# 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腳本的文法大家可以從官網檢視。

3,将 Gradle 關聯到您的原生庫

要将 Gradle 關聯到您的原生庫,您需要提供一個指向 CMake 或 ndk-build 腳本檔案的路徑。有兩種方法,1,用Android Studio UI工具 2,手動配置gradle

1)使用Android Studio UI ,步驟如下:

  1. 從 IDE 左側打開 Project 窗格并選擇 Android 視圖。
  2. 右鍵點選您想要關聯到原生庫的子產品(例如 app 子產品),并從菜單中選擇 Link C++ Project with Gradle。您應看到一個如圖 4 所示的對話框。
  3. 從下拉菜單中,選擇 CMake 或 ndk-build。
    1. 如果您選擇 CMake,請使用 Project Path 旁的字段為您的外部 CMake 項目指定 

      CMakeLists.txt

       腳本檔案。
    2. 如果您選擇 ndk-build,請使用 Project Path 旁的字段為您的外部 ndk-build 項目指定 

      Android.mk

       腳本檔案。如果 Application.mk 檔案與您的 Android.mk 檔案位于相同目錄下,Android Studio 也會包含此檔案。
    android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇
  4. 點選 ok

2)手動配置 Gradle,步驟如下:

要手動配置 Gradle 以關聯到您的原生庫,您需要将 

externalNativeBuild {}

 塊添加到子產品級 build.gradle 檔案中,并使用 cmake {} 

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

如果想指定 ABI 可添加如下代碼:

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

4,這時候就可以在代碼中引用native-lib.so 包,并調用原生代碼了

直接上代碼,不解釋:

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();
}
      

好了,現在你可以運作程式了。

四、總結:

寫部落格還是很累的事情,有寫得不好的地方,還請諒解。啦啦啦.... 今天就這樣結束了,結束前好像還缺點兒什麼東西, 什麼東西呢......

那當然是代碼啦:項目下載下傳請點選我哦

繼續閱讀