android NDK 如何使用 JNI 与 Java&Kotlin 语言交互?
http://web.archive.org/web/20120626135526/http://java.sun.com/docs/books/jni/html/jniTOC.html
主要三种情况
extern "C" JNIEXPORT void JNICALLn ame(JNIEnv *env, jobject thiz){}
情况 1 直接使用即可。
情况 2 通过 android Global JavaVM 获取当前 JNIEnv,例如使用函数 jint GetEnv(JavaVM *vm, void **env, jint version);
。
情况3 通过 android Global JavaVM 获取当前 JNIEnv,例如使用函数 jint AttachCurrentThread(JavaVM *vm, void **p_env, void *thr_args);
jint DetachCurrentThread(JavaVM *vm);
;值得注意的是,线程中需要使用全局引用对象来获取类id。这可以在情况1中使用 jobject NewGlobalRef(JNIEnv *env, jobject obj);
函数获取一个全局引用对象。
引用类型在其他线程调用时,需要使用 jobject NewGlobalRef(JNIEnv *env, jobject obj);
函数获取一个全局引用,然后在其他线程使用这个全局引用即可。
/*
* Reference types, in C++
*/
class _jobject {};
class _jclass : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jobjectArray : public _jarray {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jthrowable : public _jobject {};
jni 如何设置 java 类中的枚举成员变量值 ?
// 调用四个 jni 函数即可完成枚举值设置
// auto cls = env->FindClass(); // CLASS ENUM
// auto enum_field = env->GetStaticFieldID(); // CLASS ENUM FIELD
// auto enum_field_object = env->GetStaticObjectField(cls,enum_field); // Get Enum Value
// env->SetObjectField(obejct,field,enum_field_object); // Set Enum Field Value
jni 如何返回 java enum 类型?
// 调用三个函数即可直接从 jni 返回 java enum 类型
env->FindClass();
env->GetStaticFieldID();
auto obj = env->GetStaticObjectField();
return obj; // 直接返回该 jobject 即可 , java enum 作为静态类型存在。
具体代码如下所示(中间略有省略非必要代码,不过核心代码基本都在,基本流程已经过测试,可正常在主线程和非主线程与 java/kotlin语言交互):
#include <jni.h>
#include <android/log.h>
#include <map>
#include <string>
#include <thread>
// https://docs.oracle.com/javase/8/docs/api/overview-summary.html
// https://docs.oracle.com/en/java/javase/12/docs/specs/jni/index.html
// https://developer.android.com/ndk
// external fun name(); // kotlin
// native void name(); // Java
struct Context{
JavaVM *vm;
std::map<std::string,jobject> dict;
} GlobalContext;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *unused) {
GlobalContext.vm = vm;
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
__android_log_print(ANDROID_LOG_VERBOSE, "main:", "VM address %p env %p\n",vm,env);
return JNI_ERR;
}
// call init
return JNI_VERSION_1_6;
}
// 主线程调用 java/kotlin 静态方法
// kotlin
// @Keep
// @JvmStatic
void call_java_static_function(JavaVM *vm) {
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
__android_log_print(ANDROID_LOG_VERBOSE, "main:", "JVM address %p env %p\n",vm,env);
}
// env->FindClass()
// env->GetMethodID()
// env->CallStaticVoidMethod()
}
class Task {
public:
Task(Context *ctx){
this->ctx = ctx;
}
void operator()() {
// other thread
JNIEnv *env;
if(ctx->vm->AttachCurrentThread(&env, NULL) != JNI_OK){
__android_log_print(ANDROID_LOG_VERBOSE, "main:","Not AttachCurrentThread JNIEnv\n");
return;
}
// NewGlobalRef
// DeleteGlobalRef
auto obj = ctx->dict.at("install");
// env->GetObjectClass(obj)
// env->GetMethodID()
// env->CallStaticVoidMethod()
ctx->vm->DetachCurrentThread();
}
private:
Context *ctx;
};
// 其他线程调用 java/kotlin 静态方法
// kotlin
// @Keep
// @JvmStatic
void call_java_static_function_of_other_thread(Context *ctx) {
std::thread run((Task(ctx)));
run.detach();
}
打印日志信息
#include "android/log.h"
struct Tag {
std::string tag;
const char *data() const {
return tag.c_str();
}
};
template<typename... Args>
void print(Tag tag,Args &&... args) {
__android_log_print(ANDROID_LOG_DEBUG,tag.data(),args...);
}