android NDK 如何使用 JNI 与 Java&Kotlin 语言交互?

叶稳
2023-12-01

android NDK 如何使用 JNI 与 Java&Kotlin 语言交互?

http://web.archive.org/web/20120626135526/http://java.sun.com/docs/books/jni/html/jniTOC.html

主要三种情况

  1. 自带 JNIEnv 上下文,例如这种参数声明 extern "C" JNIEXPORT void JNICALLn ame(JNIEnv *env, jobject thiz){}
  2. 主线程反射调用 java/kotlin
  3. 其他线程反射调用 java/kotlin

情况 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...);
}
 类似资料: