本文用一個demo驗證Android Studio如何編譯生成一個C++動態庫檔案(so檔案) 給 Java應用層使用,然后這個so庫內部又如何呼叫 一個預有so庫中的 C語言函式,
1. 新建一個NDK工程
1.1 新建一個android studio Native C++工程

在main/cpp目錄下默認有一個native-lib.cpp,還有一個CMakeList.txt檔案(編譯腳本),其中
native-lib.cpp實作了從JNI里回傳一個C++字串,具體代碼如下:
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring
Java_com_example_ndktest_MainActivity_stringFromJNI(JNIEnv* env,jobject) {
std::string hello = "Hello from cppmain";
return env->NewStringUTF(hello.c_str());
}
其中env->NewStringUTF(hello.c_str());將C++字串hello轉換成了jstring型別(JNI可以傳遞給JAVA的字串型別)
1.2 配置build.gradle
我們把CMakeList.txt移動到app moudle的根目錄下,即與app模塊下的build.gradle放在同一目錄下,在以下腳本3中設定cmake中的path(CMakeList.txt編譯腳本的存放路徑),path值直接就是CMakeList.txt,
android--defaultcofig--下添加如下腳本
腳本1:
externalNativeBuild {
cmake {
abiFilters 'armeabi-v7a'
}
}
注解:表示只編譯armeabi-v7a CPU架構下已經存放的的so庫,
腳本2:
ndk{
abiFilters 'armeabi-v7a'
}
注解:表示只生成armeabi-v7a CPU架構下的so庫,并打包到APP里,
同時,添加這個腳本是為了解決這個問題:用android studio直接運行專案會報錯,找不到最終生成的libnative-lib.so,只有build生成apk,然后傳到手機上運行才不報錯,
添加了這個ndk腳本后就一切正常了,具體原因還需進一步研究下,
腳本3(在android配置下添加):
externalNativeBuild {
cmake {
path "CMakeLists.txt"
version "3.10.2"
}
}
注解:其中path是CMakeLists編譯腳本檔案的路徑,以 path "CMakeLists.txt"為例,表示CMakeLists.txt檔案在android studio工程的主app模塊根目錄下,與app模塊的build.gradle檔案在同一目錄下,version "3.10.2"使用的CMake編譯工具的版本,
2. 在native-lib.cpp中的stringFromJNI數里呼叫另一個so庫libTest.so中的test()方法
2.1 修改native.cpp的stringFromJNI函式:呼叫test()
#include <jni.h>
#include <string>
#include <android/log.h>
extern "C"{
extern int test();
}
extern "C" JNIEXPORT jstring
Java_com_example_ndktest_MainActivity_stringFromJNI(JNIEnv* env,jobject) {
std::string hello = "Hello from cppmain";
__android_log_print(ANDROID_LOG_ERROR,"jni","libtest.so 里面的 test 方法:%d",test());
return env->NewStringUTF(hello.c_str());
}
(1)相比之前加了這樣代碼: __android_log_print(ANDROID_LOG_ERROR,"jni","libTest.so 里面的 test 方法:%d",test()); 折行代碼用于列印日志,輸出test()方法回傳的int值,
(2)要呼叫test()方法,首先得用extern宣告外部原型,因為test()函式的實作是在其它C語言檔案里,加了一個extern "C"是因為test()是一個C語言程式,要在C++里呼叫C代碼必須得宣告關鍵字extern "C".
2.2 拷貝libTest.so庫到我們新建的NDK專案中
將libTest.so復制到src/main/jniLibs/armeabi-v7a目錄下,我們在此只編譯armeabi-v7a CPU架構的so庫
2.3 修改CMakeList.txt編譯腳本,引入第三方庫libTest.so,并鏈接到本地要生成的libnative-lib.so庫
腳本如下:
cmake_minimum_required(VERSION 3.10.2)
#宣告專案名
project("ndktest")
#將.C檔案與C++(就是cpp)檔案的查找路徑賦值給一個變數source,多個檔案路徑用空格隔開,
file(GLOB source src/main/cpp/*.c src/main/cpp/*.cpp src/main/cpp/a/*.cpp )
#將C/C++原始碼編譯,生成一個本地so動態庫,庫名是native-lib,原始碼路徑就是上面那個source變數的值,${source}取值,SHARED表示將cmake要編譯生成的動態庫(so檔案)
add_library( # Sets the name of the library.
native-lib
SHARED
${source}) # 在此專案中也可直接在此填寫 src/main/cpp/native-lib.cpp
# 將下面的log庫的路徑賦值給log-lib, 用于target_link_libraries中鏈接到將要生成的本地native- lib庫
find_library(
log-lib
log # log庫的路徑,因為log為系統庫,所以只需要名字,cmake就知道其路徑了
)
#配置第三方Test.so庫的鏈接路徑,我們的Test.so庫放在了armeabi-v7a下,2.1中已經提到,
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")
#將Test庫,log庫與本地將要生成的動態庫鏈接在一起,這樣native-lib庫里的stringFromJNI函式就可以呼叫test()函式與log庫中的__android_log_print()函式了,
target_link_libraries( # Specifies the target library.
native-lib
Test
# Links the target library to the log library included in the NDK.
${log-lib}
)
3. 在java層呼叫本地庫native-lib.so中的JNI函式stringFromJNI
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
public native String stringFromJNI();
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/297325.html
標籤:其他
