上一篇部落格,分享了 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 項目類似,不過前者還需要額外幾個步驟:
- 在向導的 Configure your new project 部分,選中 Include C++ Support 複選框。
android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇 - 點選 Next。
- 正常填寫所有其他字段并完成向導接下來的幾個部分。
- 在向導的 Customize C++ Support 部分,您可以使用下列選項自定義項目:
android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇 - C++ Standard:使用下拉清單選擇您希望使用哪種 C++ 标準。選擇 Toolchain Default 會使用預設的 CMake 設定。
- Exceptions Support:如果您希望啟用對 C++ 異常處理的支援,請選中此複選框。如果啟用此複選框,Android Studio 會将
标志添加到子產品級 build.gradle 檔案的 cppFlags 中,Gradle 會将其傳遞到 CMake。</li><li><strong>Runtime Type Information Support</strong>:如果您希望支援 RTTI,請選中此複選框。如果啟用此複選框,Android Studio 會将 <code>-frtti 标志添加到子產品級 build.gradle 檔案的 cppFlags 中,Gradle 會将其傳遞到 CMake。-fexceptions
- 點選 Finish。
在 Android Studio 完成新項目的建立後,請從 IDE 左側打開 Project 窗格并選擇 Android 視圖。如圖 下圖 2-1 所示,Android Studio 将添加 cpp 和 External Build Files 組。Project 視圖如下:2-3所示。
圖 2-1
點選 Run 先運作項目看效果:如下圖 2-2 ,在 app/build/intrmediates/cmake/debug/obj下面可以看到libnative-lib.so檔案了,如下圖: 2-3
圖 2-2
圖: 2-3
我們再分析一下跟我們平時開發不帶ndk的項目的不同點
主要有四個改變:如下圖所示:
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();
}
下面的概覽介紹了建構和運作示例應用時會發生的事件:
- Gradle 調用您的外部建構腳本
。CMakeLists.txt
- CMake 按照建構腳本中的指令将 C++ 源檔案
編譯到共享的對象庫中,并命名為 libnative-lib.so,Gradle 随後會将其打包到 APK 中。native-lib.cpp
- 運作時,應用的
會使用 System.loadLibrary() 加載原生庫。現在,應用可以使用庫的原生函數 stringFromJNI()。MainActivity
-
調用 stringFromJNI(),這将傳回“Hello from C++”并使用這些文字更新 TextView。MainActivity.onCreate()
說了這麼多,最好上代碼:項目下載下傳請點選我哦
三、向現有的項目添加C/C++代碼
如果你想向現有的項目添加原生代碼,請看下面的步驟
- 建立新的原生源檔案cpp檔案夾以及源檔案 并将其添加到您的 Android Studio 項目中。
- 建立 CMake 建構腳本,将您的原生源代碼建構到庫中。
- 提供一個指向您的 CMake ,将 Gradle 關聯到您的原生庫。Gradle 使用建構腳本将源代碼導入您的 Android Studio 項目并将原生庫(SO 檔案)打包到 APK 中。
配置完項目後,您可以使用 JNI 架構從 Java 代碼中通路您的原生函數。要建構和運作應用,隻需點選 Run
。Gradle 會以依賴項的形式添加您的外部原生建構流程,用于編譯、建構原生庫并将其随 APK 一起打包。
1, 建立新的原生源檔案
要在應用子產品的主源代碼集中建立一個包含建立原生源檔案的 cpp 目錄,請按以下步驟操作:
- 從 IDE 的左側打開 Project 窗格并從下拉菜單中選擇 Project 視圖。
- 導航到 您的子產品 > src,右鍵點選 main 目錄,然後選擇 New > Directory。
- 為目錄輸入一個名稱(例如
)并點選 OK。cpp
- 右鍵點選您剛剛建立的目錄,然後選擇 New > C/C++ Source File。
- 為您的源檔案輸入一個名稱,例如
。native-lib
- 從 Type 下拉菜單中,為您的源檔案選擇檔案擴充名,例如
。.cpp
- 點選 Edit File Types ,您可以向下拉菜單中添加其他檔案類型,例如
android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇
或 .hxx。在彈出的 C/C++ 對話框中,從 Source Extension 和 Header Extension 下拉菜單中選擇另一個檔案擴充名,然後點選 OK。.cxx
- 點選 Edit File Types
- 如果您還希望建立一個标頭檔案,請選中 Create an associated header 複選框。
- 點選 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 ,步驟如下:
- 從 IDE 左側打開 Project 窗格并選擇 Android 視圖。
- 右鍵點選您想要關聯到原生庫的子產品(例如 app 子產品),并從菜單中選擇 Link C++ Project with Gradle。您應看到一個如圖 4 所示的對話框。
- 從下拉菜單中,選擇 CMake 或 ndk-build。
- 如果您選擇 CMake,請使用 Project Path 旁的字段為您的外部 CMake 項目指定
腳本檔案。CMakeLists.txt
- 如果您選擇 ndk-build,請使用 Project Path 旁的字段為您的外部 ndk-build 項目指定
腳本檔案。如果 Application.mk 檔案與您的 Android.mk 檔案位于相同目錄下,Android Studio 也會包含此檔案。Android.mk
android studio cMake腳本添加ndk,建構C/C++程式(基礎使用篇 - 如果您選擇 CMake,請使用 Project Path 旁的字段為您的外部 CMake 項目指定
- 點選 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();
}
好了,現在你可以運作程式了。
四、總結:
寫部落格還是很累的事情,有寫得不好的地方,還請諒解。啦啦啦.... 今天就這樣結束了,結束前好像還缺點兒什麼東西, 什麼東西呢......
那當然是代碼啦:項目下載下傳請點選我哦