【第一步】 編寫C++代碼
Test.cpp
int test() {
return 10086;
}
【第二步】 生成so庫
在AS中配置好cmake環(huán)境,將 Test.cpp 放入工程,編譯出 so 庫。(第一章節(jié)有詳細(xì)介紹)
【第三步】 聲明 extern int test(),并調(diào)用test函數(shù)
#include <jni.h>
#include <string>
#include <pthread.h>
#include <android/log.h>
#define LOG_TAG "native-lib"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
using namespace std;
// 如果是C,需要添加 extern "C"{}
extern int test();
// 全局變量
JavaVM* javaVm;
struct Context {
jobject obj;
};
/**
* 子線程
* @param args
* @return
*/
void* threadTask(void* args) {
LOGI("start thread task");
LOGE("執(zhí)行test函數(shù):%d", test());
if (javaVm == NULL) {
LOGE("javaVm is null");
return JNI_FALSE;
}
JNIEnv* env;
// 將native線程添加到JVM中
jint isAttach = javaVm->AttachCurrentThread(&env, 0);
if (isAttach != JNI_OK) {
LOGE("attact thread error");
return JNI_FALSE;
}
Context* context = static_cast<Context *>(args);
// 得到字節(jié)碼
jclass clazz = env->GetObjectClass(context->obj);
// 得到methodId
jmethodID methodId = env->GetMethodID(clazz, "updateUI", "()V");
// 執(zhí)行方法
env->CallVoidMethod(context->obj, methodId);
// 分離
javaVm->DetachCurrentThread();
delete context;
context = NULL;
return JNI_FALSE;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void*) {
LOGI("in JNI_Onload");
javaVm = vm;
return JNI_VERSION_1_6;
}
extern "C" JNIEXPORT void JNICALL
Java_com_nobug_jniproject_MainActivity_testThread(JNIEnv *env, jobject obj) {
LOGI("testThread from C");
Context* context = new Context;
context->obj = env->NewGlobalRef(obj);
pthread_t pid;
// 啟動一個線程
pthread_create(&pid, 0,threadTask, context);
}
【第四步】 Java 調(diào)用 testThread
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private JNI jni = new JNI();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.sampleText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
testThread();
}
});
}
public native void testThread();
public void updateUI() {
if (Looper.getMainLooper() == Looper.myLooper()) {
Toast.makeText(MainActivity.this, "updateUI", Toast.LENGTH_SHORT).show();
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "updateUI", Toast.LENGTH_SHORT).show();
}
});
}
}
}
【第五步】 注意Gradle配置
plugins {
id 'com.android.application'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.nobug.jniproject"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
abiFilters 'armeabi-v7a'
cppFlags "-std=c++11 -frtti -fexceptions -Os -Wall"
}
}
ndk { // "armeabi-v7a", "arm64-v8a"
abiFilters 'armeabi-v7a'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
externalNativeBuild {
cmake {
path file('CMakeLists.txt')
version '3.18.1'
}
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
【第六步】 cmake配置
cmake_minimum_required(VERSION 3.18.1)
# project("jniproject")
# 導(dǎo)入庫目錄
# include_directories("C:/Program Files/Java/jdk-18.0.1.1/include")
# 生成一個動態(tài)庫(windows:dll,android:so)
add_library(jniproject SHARED src/main/cpp/native-lib.cpp)
# 設(shè)置一個變量
# CMAKE_CXX_FLAGS c++參數(shù)
# CMAKE_C_FLAGS c參數(shù)
# CMAKE_C_FLAGS = --sysroot=XX
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")
#add_library(Test SHARED IMPORTED)
#set_target_properties(Test PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libTest.so)
# 生成可執(zhí)行文件(生成exe文件, 在VS工具上可用)
# add_executable (jniproject1 native-lib.cpp)
# log-lib 是變量名稱 log是動態(tài)庫名稱 將liblog.so或liblog.a的路徑賦值給log-lib
find_library(log-lib log)
# 鏈接ndk自帶的庫
target_link_libraries(
jniproject # 鏈接 libjniproject.so
Test # 鏈接 libTest.so
${log-lib}) # 鏈接 liblog.so
其中
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")
可以改成:
add_library(Test SHARED IMPORTED)
set_target_properties(Test PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libTest.so)
【第七步】 運(yùn)行app,查看工作臺中是否有相關(guān)日志

image.png
[本章完...]