Android Studio C++/JNI/Kotlin 示例 三
Android JNI 回调示例,展示了本地 C++ 代码如何通过 JNI 回调 Java 层接口。
MainActivity.kt
package com.demo.learn2import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.activity.ComponentActivityclass MainActivity : ComponentActivity() {// 用于存储本地对象的指针private var nativeWorkerPtr: Long = 0 // 初始化为 0// 加载本地库init {System.loadLibrary("native_code")}// 本地方法声明external fun startNativeThread(callback: NativeCallback)external fun stopNativeThread()// 回调接口interface NativeCallback {fun onProgressUpdate(progress: Int)fun onMessageReceived(message: String)fun onError(errorCode: Int, errorMessage: String)}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 创建回调实现val callback = object : NativeCallback {override fun onProgressUpdate(progress: Int) {Log.d("JNICallback", "Progress updated: $progress%")}override fun onMessageReceived(message: String) {Log.d("JNICallback", "Message received: $message")}override fun onError(errorCode: Int, errorMessage: String) {Log.e("JNICallback", "Error $errorCode: $errorMessage")}}// 启动本地线程startNativeThread(callback)// 10秒后停止线程(示例)Handler(Looper.getMainLooper()).postDelayed({stopNativeThread()}, 10000)}
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_STANDARD 11) # 启用C++11支持project("native_code") #定义项目名称为 "native_code"
add_library(native_codeSHAREDnative_code.cppnative_code.h
)#设置头文件搜索路径
target_include_directories(native_code # 目标库名称PRIVATE # 表示这些路径仅用于构建该库${CMAKE_SOURCE_DIR} # CMake 变量,表示项目根目录
)find_library(log-liblog
)target_link_libraries(native_codeandroid # 链接 Android NDK 平台库${log-lib}
)
native_code.h
#ifndef LEARN2_NATIVE_CODE_H
#define LEARN2_NATIVE_CODE_H#include <jni.h>
#include <string>
#include <thread>
#include <atomic>class NativeWorker {
public:NativeWorker(JNIEnv* env, jobject callback);~NativeWorker();void start();void stop();bool isRunning() const;private:void run();JNIEnv* getJNIEnv(bool* attached);void detachJNIEnv(bool attached);JavaVM* jvm;jobject javaCallbackRef;jmethodID onProgressUpdateMethod;jmethodID onMessageReceivedMethod;jmethodID onErrorMethod;std::thread workerThread;std::atomic<bool> running;
};#endif //LEARN2_NATIVE_CODE_H
NativeWorker 类定义
1. 公共接口
构造函数:接收 JNI 环境和 Java 回调对象
析构函数:负责资源清理
start():启动工作线程
stop():停止工作线程
isRunning():检查线程是否在运行
2. 私有成员
2.1 核心方法
run():工作线程的主逻辑
getJNIEnv():获取当前线程的 JNI 环境
detachJNIEnv():从当前线程分离 JVM
2.2 JNI 相关成员
jvm:保存 JavaVM 引用,用于后续获取 JNIEnv
javaCallbackRef:Java 回调对象的全局引用
onXXXMethod:三个 Java 回调方法的 ID
2.3 线程控制成员
workerThread:工作线程对象
running:原子布尔标志,用于线程安全地控制线程运行状态
3.关键设计要点
3.1 JNI 环境管理
JavaVM 保存:在构造函数中保存 JavaVM,用于后续在任何线程获取 JNIEnv
全局引用:将 Java 回调对象转换为全局引用,防止被垃圾回收
方法 ID 缓存:提前获取方法 ID 提升性能
3.2 线程安全设计
原子标志:使用
std::atomic<bool>
确保running
标志的线程安全访问线程生命周期:通过
start()
/stop()
明确控制线程生命周期资源清理:析构函数确保线程停止和资源释放
3.3 跨线程回调机制
全局引用:允许在不同线程回调 Java 方法
JNIEnv 获取:
getJNIEnv()
处理线程附加/分离
4. 典型使用场景
// JNI 函数中创建 worker
NativeWorker* worker = new NativeWorker(env, callback);
worker->start();// ...worker->stop();
delete worker;
native_code.cpp
#include "native_code.h"
#include <android/log.h>#define LOG_TAG "NativeCode"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)NativeWorker::NativeWorker(JNIEnv* env, jobject callback) : running(false) {// 保存JavaVM引用env->GetJavaVM(&jvm);// 创建全局引用,防止被垃圾回收javaCallbackRef = env->NewGlobalRef(callback);// 获取回调方法IDjclass callbackClass = env->GetObjectClass(callback);onProgressUpdateMethod = env->GetMethodID(callbackClass, "onProgressUpdate", "(I)V");onMessageReceivedMethod = env->GetMethodID(callbackClass, "onMessageReceived", "(Ljava/lang/String;)V");onErrorMethod = env->GetMethodID(callbackClass, "onError", "(ILjava/lang/String;)V");if (!onProgressUpdateMethod || !onMessageReceivedMethod || !onErrorMethod) {LOGE("Failed to get method IDs");}
}NativeWorker::~NativeWorker() {stop();bool attached = false;JNIEnv* env = getJNIEnv(&attached);if (env) {env->DeleteGlobalRef(javaCallbackRef);}detachJNIEnv(attached);
}void NativeWorker::start() {if (running) return;running = true;workerThread = std::thread(&NativeWorker::run, this);
}void NativeWorker::stop() {if (!running) return;running = false;if (workerThread.joinable()) {workerThread.join();}
}bool NativeWorker::isRunning() const {return running;
}//获取当前线程的 JNI 环境,必要时附加到 JVM
JNIEnv* NativeWorker::getJNIEnv(bool* attached) {*attached = false;JNIEnv* env = nullptr;// 获取当前线程的JNIEnvjint status = jvm->GetEnv((void**)&env, JNI_VERSION_1_6);if (status == JNI_EDETACHED) {// 如果当前线程未附加到JVM,附加它if (jvm->AttachCurrentThread(&env, nullptr) == JNI_OK) {*attached = true;} else {LOGE("Failed to attach thread to JVM");return nullptr;}} else if (status != JNI_OK) {LOGE("Failed to get JNIEnv, status: %d", status);return nullptr;}return env;
}//如果需要,从当前线程分离 JVM
void NativeWorker::detachJNIEnv(bool attached) {if (attached) {jvm->DetachCurrentThread();}
}void NativeWorker::run() {int progress = 0;while (running && progress < 100) {// 模拟工作std::this_thread::sleep_for(std::chrono::milliseconds(200));progress += 10;bool attached = false;JNIEnv* env = getJNIEnv(&attached);if (!env) {LOGE("Failed to get JNIEnv in worker thread");continue;}// 回调进度更新env->CallVoidMethod(javaCallbackRef, onProgressUpdateMethod, progress);// 检查是否有异常if (env->ExceptionCheck()) {env->ExceptionDescribe();env->ExceptionClear();LOGE("Exception occurred during callback");}// 每隔几次发送消息if (progress % 20 == 0) {jstring message = env->NewStringUTF("Progress milestone reached");env->CallVoidMethod(javaCallbackRef, onMessageReceivedMethod, message);env->DeleteLocalRef(message);if (env->ExceptionCheck()) {env->ExceptionDescribe();env->ExceptionClear();LOGE("Exception occurred during message callback");}}detachJNIEnv(attached);}// 工作完成或停止bool attached = false;JNIEnv* env = getJNIEnv(&attached);if (env) {if (progress >= 100) {jstring message = env->NewStringUTF("Work completed");env->CallVoidMethod(javaCallbackRef, onMessageReceivedMethod, message);env->DeleteLocalRef(message);} else {jstring message = env->NewStringUTF("Work stopped");env->CallVoidMethod(javaCallbackRef, onMessageReceivedMethod, message);env->DeleteLocalRef(message);}if (env->ExceptionCheck()) {env->ExceptionDescribe();env->ExceptionClear();}detachJNIEnv(attached);}
}// JNI函数实现
extern "C" {JNIEXPORT void JNICALLJava_com_demo_learn2_MainActivity_startNativeThread(JNIEnv* env, jobject thiz, jobject callback) {// 创建并启动workerNativeWorker* worker = new NativeWorker(env, callback);worker->start();// 存储worker指针到Java对象(简化示例,实际应更安全地处理)jclass clazz = env->GetObjectClass(thiz);jfieldID fieldId = env->GetFieldID(clazz, "nativeWorkerPtr", "J");if (fieldId) {env->SetLongField(thiz, fieldId, reinterpret_cast<jlong>(worker));} else {LOGE("Failed to find nativeWorkerPtr field");}}JNIEXPORT void JNICALLJava_com_demo_learn2_MainActivity_stopNativeThread(JNIEnv* env, jobject thiz) {// 获取worker指针jclass clazz = env->GetObjectClass(thiz);jfieldID fieldId = env->GetFieldID(clazz, "nativeWorkerPtr", "J");if (!fieldId) {LOGE("Failed to find nativeWorkerPtr field");return;}//停止并删除对象,清除指针jlong ptr = env->GetLongField(thiz, fieldId);NativeWorker* worker = reinterpret_cast<NativeWorker*>(ptr);if (worker) {worker->stop();delete worker;env->SetLongField(thiz, fieldId, 0L);}}
}
NativeWorker 类构造函数
NativeWorker::NativeWorker(JNIEnv* env, jobject callback) : running(false) {// 保存JavaVM引用env->GetJavaVM(&jvm);// 创建全局引用,防止被垃圾回收javaCallbackRef = env->NewGlobalRef(callback);// 获取回调方法IDjclass callbackClass = env->GetObjectClass(callback);onProgressUpdateMethod = env->GetMethodID(callbackClass, "onProgressUpdate", "(I)V");onMessageReceivedMethod = env->GetMethodID(callbackClass, "onMessageReceived", "(Ljava/lang/String;)V");onErrorMethod = env->GetMethodID(callbackClass, "onError", "(ILjava/lang/String;)V");
}
构造函数接收 JNI 环境和 Java 回调对象
保存 JavaVM 引用(用于后续获取 JNIEnv)
创建回调对象的全局引用(防止被垃圾回收)
获取回调方法的 ID(用于后续调用)
NativeWorker 类析构函数
NativeWorker::~NativeWorker() {stop();bool attached = false;JNIEnv* env = getJNIEnv(&attached);if (env) {env->DeleteGlobalRef(javaCallbackRef);}detachJNIEnv(attached);
}
停止工作线程
获取 JNI 环境
删除全局引用
如果需要,从当前线程分离 JVM
工作线程主逻辑
void NativeWorker::run() {int progress = 0;while (running && progress < 100) {std::this_thread::sleep_for(std::chrono::milliseconds(200));progress += 10;bool attached = false;JNIEnv* env = getJNIEnv(&attached);if (!env) continue;// 回调进度更新env->CallVoidMethod(javaCallbackRef, onProgressUpdateMethod, progress);// 检查异常if (env->ExceptionCheck()) {env->ExceptionDescribe();env->ExceptionClear();LOGE("Exception occurred during callback");}// 每隔20%发送消息if (progress % 20 == 0) {jstring message = env->NewStringUTF("Progress milestone reached");env->CallVoidMethod(javaCallbackRef, onMessageReceivedMethod, message);env->DeleteLocalRef(message);if (env->ExceptionCheck()) {env->ExceptionDescribe();env->ExceptionClear();LOGE("Exception occurred during message callback");}}detachJNIEnv(attached);}// 工作完成或停止后的处理bool attached = false;JNIEnv* env = getJNIEnv(&attached);if (env) {jstring message = env->NewStringUTF(progress >= 100 ? "Work completed" : "Work stopped");env->CallVoidMethod(javaCallbackRef, onMessageReceivedMethod, message);env->DeleteLocalRef(message);if (env->ExceptionCheck()) {env->ExceptionDescribe();env->ExceptionClear();}detachJNIEnv(attached);}
}
模拟工作进度(每200毫秒增加10%)
定期回调 Java 方法更新进度
每20%发送一个里程碑消息
处理完成后发送最终消息
关键点总结
线程管理:使用 C++11 的
std::thread
创建本地线程JNI 环境处理:正确处理线程附加/分离 JVM
Java 回调:通过全局引用和方法 ID 安全回调 Java 方法
异常处理:检查并清除 JNI 调用可能产生的异常
内存管理:正确管理全局引用和本地对象
线程安全:使用
running
标志控制线程生命周期