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

在使用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传递进来的obj,保存到c的jniobj结构体内。

static jobject g_obj = NULL;
JNIEXPORT jint JNICALL Java_com_xxx_xxx_JNI_init(
	JNIEnv *env, 
	jobject oj, 
	jobject obj,
	)
{
	if(obj == MNull){
			MVLOG("obj is null");
		}else
		{
			MVLOG("get java obj");
			g_obj = env->NewGlobalRef(obj);//这里一般都需要转为全局变量
		}
	return res;
}

2.在c层拿到java class

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

static const char* const CLASS_NAME = "com/xxx/xxx/JNI";
jclass g_class = env->FindClass(CLASS_NAME);

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

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

###4.调用method
c:在需要调用的地方调用这个java方法,

static void error(MDWord code, MVoid* pObj)
{
	MVLOG("error is in code : %d", code );
	if(jniobj->g_ThreadEnv == MNull)
	{
		MVLOG("attach current thread start");
		//JavaVM* g_jvm 和 JNIEnv* env参数可以在java调用jni时保存起来
		//g_jvm可以通过env->GetJavaVM()获取到
		//AttachCurrentThread是为了绑定当前线程,每次c调用java方法前都需要调用
		JavaVM* g_jvm -> AttachCurrentThread(&env, MNull);
		if(env == MNull){
			MVLOG("attach current thread is error");
			return;
		}
	}
	if(jniobj && JNI_error){
		MVLOG("error is called");
		//这里是最关键的调用过程,通过JNI提供的CallVoidMethod加入参数(class引用、method引用、入参)来调用,这样调用java方法就完成了。
		env->CallVoidMethod(jniobj->g_obj, jniobj->JNI_error, (int)code);
	}
	if(g_jvm){
		MVLOG("error method detach");
		g_jvm->DetachCurrentThread();
		env = MNull;
	}
}

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

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

你可能感兴趣的:(ndk,jni,ndk,android,java,jni)