1. 程式人生 > >Android JNI-c/c++呼叫java方法

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傳遞進來的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,本著總結經驗的心態,寫下部落格稍微記錄一下,有錯誤或者不詳細的地方,也請各位大神指點。