Linux 下 JNI 实现 Java 调用 c的例子
c/cpp 调用 Java 方法的重点:
- jclass 是由 jobject public 继承而来的子类,所以它当然是一个 jobject,需要创建一个 global reference 以便日后使用。
- jmethodID/jfieldID 与 jobject 没有继承关系,它不是一个 jobject,只是个整数,所以不存在被释放与否的问题,可保存后直接使用。
- JNIEnv 是一个与线程相关的变量,不同线程的 JNIEnv 彼此独立。
- JavaVM是虚拟机在JNI层的代表,在一个虚拟机进程中只有一个JavaVM。
- 反射 : 我们可以通过类或类对象得到 Class 类的对象,反过来,我们也可以由 Class 类的对象得到类的对象。
Android CPP 调用 Java 方法的步骤:
0. 在注册 JNI 时,保存 JVM 和 jobject 本地引用
- 获取线程中 java 运行环境: gJvm->AttachCurrentThread(&env, NULL)
- 获取 Java class 类 :jclass cls = env->FindClass("com/test/JNItest");
- 获取 java 的方法 ID : jmethodID jcallback=env->GetMethodID(jcls,"PreviewCallback","()V");
- 调用 java 方法(区分 静态和非静态方法): env->CallVoidMethod(gJobj, jcallback);
- 释放线程中 java 运行环境 : gJvm->DetachCurrentThread()
例如:
一. 在注册 JNI 时,保存 JVM 和 jobject 本地引用
// 全局静态变量
static JavaVM *gJvm = NULL;
static jobject gJobj = NULL;
// 反射是相对极为消耗资源的,建议使用全局变量保存经常会使用的资源
//static jclass gJcls = NULL;
//static jmethodID gJmethodID = NULL;
// 初始化
JNIEXPORT jint nativeInit(JNIEnv *env, jobject obj)
{
if (obj == NULL)
{
return -1;
}
env->GetJavaVM(&gJvm); // 获取虚拟机。并保存到 gJvm 中。也可在 JNI_OnLoad 中赋值
gJobj = env->NewGlobalRef(obj); // 创建对象的本地引用
return 0;
}
// 去初始化
JNIEXPORT jint nativeUnInit(JNIEnv *env, jobject obj)
{
cur_enter();
if(gJobj != NULL)
{
env->DeleteGlobalRef(gJobj);
gJobj = NULL;
}
return 0;
}
建议用 C 的格式声明 jni 函数
#ifndef JNI_TEST_H
#define JNI_TEST_H
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint nativeInit(JNIEnv *env, jobject obj);
JNIEXPORT jint nativeUnInit(JNIEnv *env, jobject obj);
JNIEXPORT jint nativeStart(JNIEnv *env, jobject obj);
JNIEXPORT jint nativeStop(JNIEnv *env, jobject obj);
#ifdef __cplusplus
}
#endif
#endif //JNI_NATIVE_CPP_JNI_TEST_H
二. JNI 中CPP 代码调用 JAVA 方法的步骤:
void Jnitest::ThreadLoop()
{
cur_enter();
int i = 0;
JNIEnv *env = NULL;
jclass jcls = NULL;
jmethodID jcallback= NULL;
while(mLoop)
{
i++;
sleep(1);
cur_logi("ThreadLoop state i = %d", i);
// 1. 获取线程中 java 运行环境
if(gJvm->AttachCurrentThread(&env, NULL) != JNI_OK)
{
cur_loge("%s: AttachCurrentThread() failed", __func__);
return;
}
// 2. 获取 Java class 类
jcls = env->GetObjectClass(gJobj); // 获取 class
if(jcls != NULL)
{
cur_logi("find jcls success");
// 3. 获取 java 的方法 ID methodID
jcallback = env->GetMethodID(jcls,"PreviewCallback", "()V");
// 4. 调用 java 方法
if(jcallback != NULL)
{
env->CallVoidMethod(gJobj, jcallback); // 调用Java方法
}
}
// 5. 释放 java 运行环境
if(gJvm->DetachCurrentThread() != JNI_OK)
{
cur_loge("%s: DetachCurrentThread() failed", __func__);
}
}
cur_logi("ThreadLoop exit");
}
三. Cap.java 注册 JNI native 方法
public class Cap
{
static
{
System.loadLibrary("native");
}
private int mNativeContext;
private final static String TAG = "myjnitest";
public native int nativeInit();
public native int nativeUnInit();
public native int nativeStart();
public native int nativeStop();
// CPP 调用 JAVA
public void PreviewCallback()
{
Log.d(TAG, "PreviewCallback OK");
};
}
四. 运行结果:
下载链接: Android ndk cpp 线程运行 java 方法