Android JNI-c/c++调用java方法

时间:2020-11-28 20:19:38

在使用ndk开发的时候,java调用c/c++方法是必须要的。但是很多时候,c/c++有callback需要反馈给java的时候(比如IM通讯登录成功信息和一些异常信息),就需要c/c++调用java方法了。
在看这篇文章之前,必须对JNI有一些基础的了解,比如java调用c/c++方法,java和c/c++ jni在一些基本类型上的对应(int对应jint等)。
那么现在介绍一下c/c++调用java方法的基本步骤:

1.需要把java方法所在类的实例通过JNI方法传到c/c++

java:JNI, 这是c需要回调的java方法,然后通过调用自身init()方法,把java实例传到c层

class JNI {
public native void init(JNI obj);
public void error(int code) {
Log.i("JNI", "c++ call error ");
}
}

c:这里把java传递进来的objListener,保存到c的jniobj结构体内。

JNIEXPORT jint JNICALL Java_com_arcvideo_rtcmessage_JNI_init(
JNIEnv *env,
jobject oj,
jobject objListener,
)
{
if(objListener == MNull){
MVLOG("objListener is null");
}else
{
MVLOG("get java obj");
jniobj->g_obj = env->NewGlobalRef(objListener);
}
return res;
}
typedef struct _tagJNIObj{//这个是刚才保存java实例的结构体,在还有其他参数
jmethodID JNI_error;
JavaVM* g_jvm;
JNIEnv* g_ThreadEnv;
jclass g_class;
jobject g_obj;
MHandle g_h;
}JNIObj;
static JNIObj* jniobj = MNull;

2.在c层拿到java class

c:通过jni提供的FindClass方法和完整类名,可以拿到class引用

static const char* const DL_CLASS_NAME = "com/arcvideo/rtcmessage/JNI";
jniobj->g_class = env->FindClass(DL_CLASS_NAME);

3.在c层拿到java method

c:根据刚才拿到的java class引用有jni提供的GetMethodID方法,和方法名,入参,就可以拿到method引用

// error
jniobj->JNI_error = env->GetMethodID(jniobj->g_class, "error",
"(I)V");
if(jniobj->JNI_error == MNull){
MVLOG("create JNI_error is error");
}

4.调用method

c:在需要调用的地方调用这个java方法,

static void error(MDWord code, MVoid* pObj)
{
MVLOG("RtcMessageJNI error is in code : %d", code );
if(jniobj->g_ThreadEnv == MNull)
{
MVLOG("attach current thread start");
jniobj->g_jvm -> AttachCurrentThread(&jniobj->g_ThreadEnv, MNull);

if(jniobj->g_ThreadEnv == MNull){
MVLOG("attach current thread is error");
return;
}
}
if(jniobj && jniobj->JNI_error){
MVLOG("RtcMessageJNI error is called");
//这里是最关键的调用过程,通过JNI提供的CallVoidMethod,来调用,加入参数,class引用,method应用,已经入参,这样调用java方法就完成了。
jniobj->g_ThreadEnv->CallVoidMethod(jniobj->g_obj, jniobj->JNI_error,
(int)code);
}

if(jniobj->g_jvm){
MVLOG("RtcMessageJNI error method detach");
jniobj->g_jvm->DetachCurrentThread();
jniobj->g_ThreadEnv = MNull;
}
}

这里在调用java方法的时候调用了,AttachCurrentThread和DetachCurrentThread方法,这是必须的,如果不调用AttachCurrentThread就拿不到线程的引用,会报错误。然后在调用结束的时候要调用DetachCurrentThread,也就是释放线程。根据个人经验,最好每次调用java方法结束的时候都调用DetachCurrentThread,这样基本不会出错。

本人也就是刚接触jni,本着总结经验的心态,写下博客稍微记录一下,有错误或者不详细的地方,也请各位大神指点。