初識(shí)安卓JNI開發(fā),ndk-build+Android.mk+Application.mk實(shí)現(xiàn)JNI的調(diào)用(一)
前一篇文章講解到使用Android.mk和Aplication.mk完成JNI的C端的調(diào)用,本篇文章用另一種方式,使用Cmake構(gòu)建jni項(xiàng)目,AS 2.2之后工具中增加了對(duì)CMake的支持,官方也推薦用CMake+CMakeLists.txt的方式,代替ndk-build+Android.mk+Application.mk的方式去構(gòu)建JNI項(xiàng)目。
本篇文章基于Android Studio2.2以上版本進(jìn)行開發(fā)講解,本篇文章使用的版本為Android Studio3.1.0。
Cmake構(gòu)建項(xiàng)目實(shí)現(xiàn)JNI的調(diào)用
1 首先我們需要構(gòu)建c/c++支持的項(xiàng)目:

這里我們選擇了Native C++進(jìn)行了創(chuàng)建項(xiàng)目,代表我們的項(xiàng)目支持針對(duì)Native的調(diào)用。

同時(shí)可以看到我們的項(xiàng)目結(jié)構(gòu),相對(duì)在main目錄下增加了cpp這個(gè)目錄,目錄中包含CMakeLists.txt和native-lib.cpp文件。在這里我們 將會(huì)主要對(duì)這兩個(gè)文件進(jìn)行相關(guān)配置
2 查看native-lib.cpp文件:
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jnitest_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
這里的native-lib.cpp為即將被編譯生產(chǎn)動(dòng)態(tài)庫(kù)的cpp文件 ,這里實(shí)為創(chuàng)建項(xiàng)目時(shí)候?yàn)槲覀兩傻哪J(rèn)文件,根據(jù)Java_com_example_jnitest_MainActivity_stringFromJNI可以看到加載文件以及調(diào)用方法應(yīng)該在MainActivity中:
package com.example.jnitest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
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 = 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();
}
2 查看CMakeLists.txt并進(jìn)行相關(guān)配置:
# 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).
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(VERSION 3.4.1) :編譯本地庫(kù)時(shí)我們需要的最小的cmake版本
add_library :相當(dāng)于Android.mk文件, native-lib 設(shè)置生成本地庫(kù)的名字;SHARED 生成庫(kù)的類型;native-lib.cpp 需要的cpp文件路徑,這里和CMakeLists.txt在同一個(gè)目錄下所以直接進(jìn)行引用。否則應(yīng)用絕對(duì)路徑 src/main/cpp/native-lib.cpp;
find_library:添加一些我們?cè)诰幾g我們的本地庫(kù)的時(shí)候需要依賴的一些庫(kù),這里是默認(rèn)用來(lái)打log的庫(kù);
target_link_libraries:關(guān)聯(lián)自己生成的庫(kù)和一些第三方庫(kù)或者系統(tǒng)庫(kù);
使用CMakeLists.txt同樣可以指定so庫(kù)的輸出路徑,但一定要在add_library之前設(shè)置,否則不會(huì)生效:
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}) #指定路徑
#生成的so庫(kù)在和CMakeLists.txt同級(jí)目錄下的libs文件夾下
如果想要配置so庫(kù)的目標(biāo)CPU平臺(tái),可以在build.gradle中設(shè)置:
android {
...
defaultConfig {
...
ndk{
abiFilters "arm64-v8a","armeabi-v7a"
}
}
...
}
設(shè)置CMakeList.txt的路徑:
android {
......
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
}
在default:
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
// 指定一些編譯選項(xiàng)
cppFlags "-std=c++11 -frtti -fexceptions"
...
// 也可以使用下面這種語(yǔ)法向變量傳遞參數(shù):
// arguments "-D變量名=參數(shù)".
arguments "-DANDROID_ARM_NEON=TRUE",
// 使用下面這種語(yǔ)法向變量傳遞多個(gè)參數(shù)(參數(shù)之間使用空格隔開):
// arguments "-D變量名=參數(shù)1 參數(shù)2"
"-DANDROID_CPP_FEATURES=rtti exceptions"
// 指定ABI
abiFilters "armeabi-v7a" , "arm64-v8a"
}
}
defaultConfig 可配置CMake下編譯的一些語(yǔ)法,CMake的語(yǔ)法規(guī)則這里不進(jìn)行詳細(xì)講解。
配置完成之后,運(yùn)行項(xiàng)目cpp目錄下生成了動(dòng)態(tài)so文件:

同時(shí)項(xiàng)目運(yùn)行完成,可以看到調(diào)用動(dòng)態(tài)庫(kù)的結(jié)果輸出:

這里我們通過(guò)Android Studio自動(dòng)創(chuàng)建的demo完成了CMake方式的一些配置工作。
現(xiàn)在我們?cè)赾pp目錄下面又新建了一個(gè)goodBye.cpp的文件。
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jnitest_MainActivity_sayGoodBye(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Bye Bye Little Java";
return env->NewStringUTF(hello.c_str());
}
新增了goodBye.cpp文件,定義了sayGoodBye方法并返回"Bye Bye Little Java"字符串。我們同時(shí)應(yīng)該把它打入到動(dòng)態(tài)so庫(kù)當(dāng)中,于是我們需要修改一下CMakeLists.txt的配置:
FILE(GLOB SRC_LIST_CPP ${PROJECT_SOURCE_DIR}/*.cpp)
FILE(GLOB SRC_LIST_C ${PROJECT_SOURCE_DIR}/*.c)
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_LIST_CPP} ${SRC_LIST_C})
可以看到我們使用的兩個(gè)FILE相關(guān)的語(yǔ)句,PROJECT_SOURCE_DIR表示當(dāng)前CMakeLists.txt所在的路徑,那么這里把此路徑下的cpp文件給遍歷存入到SRC_LIST_CPP變量中,同理SRC_LIST_C 則為當(dāng)前路徑下遍歷的c文件。而add_library則通過(guò)兩個(gè)變量把所有文件加入了進(jìn)來(lái)。此時(shí)生成的so庫(kù)文件就包含了所有的cpp和c文件。
我們運(yùn)行項(xiàng)目,點(diǎn)擊textview:

此時(shí)textview顯示調(diào)用sayGoodBye方法返回的字符串。表明so庫(kù)可被正常調(diào)用所含所有文件。
假如現(xiàn)在我們又需要把goodBye.cpp單獨(dú)放入一個(gè)so庫(kù)中呢,在項(xiàng)目中我們就會(huì)經(jīng)常根據(jù)不同功能調(diào)用打不同的so文件。
接下來(lái)我們繼續(xù)要去修改CMakeList.txt文件:
add_library( # Sets the name of the library.
hello
SHARED
native-lib.cpp)
target_link_libraries( # Specifies the target library.
hello
${log-lib})
add_library( # Sets the name of the library.
goodBye
SHARED
goodBye.cpp)
target_link_libraries( # Specifies the target library.
goodBye
${log-lib})
我們把goodBye.cpp和 native-lib.cpp分開分別生成本地庫(kù),分別取名goodBye和hello。
名字既然已經(jīng)改了,我們同時(shí)需要更改加載so庫(kù)處的代碼。
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("hello");
System.loadLibrary("goodBye");
}
}

libs中分別生成了兩份so文件,和我們配置期待的結(jié)果一致。同時(shí)再去點(diǎn)擊textview。正常則成功顯示返回的字符串。
上面簡(jiǎn)單使用Android Studio生成的JNI項(xiàng)目,修改CMakeList.txt以及相關(guān)文件,幫助我們生產(chǎn)so文件,具體CMake使用同時(shí)還有其他許多的語(yǔ)法內(nèi)容可查找相關(guān)文檔進(jìn)行查看 。