天天看點

Android JNI C/C++調用Java方法JNI環境的支援

JNI環境的支援

Android JNI程式設計從Java方法來調用native方法是比較容易的,
因為Java本身就提供了native關鍵字作為索引,隻要正确的對應Java方法和native方法的包名,做到這一步并不難.
而從C/C++方法調用Java方法則稍複雜一點,因為C/C++沒有提供跨語言調用的直接支援,是以需要由Java的JNI運作環境來提供幫助.

每一個JNI native方法的聲明一般是這樣的:
extern "C"
JNIEXPORT jstring JNICALL
Java_com_sliver_cx_android_1test_MainActivity_stringFromJNI(JNIEnv *env,jobject);

一個個關鍵字的作用是:
 extern "C"  C/C++的聲明關鍵字 表明這個jni方法是按照C的文法進行編譯的.
 JNIEXPORT   在jni的固件代碼中這樣聲明
#define JNIEXPORT  __attribute__ ((visibility ("default")))
visibility ("default")意味着GCC在生成動态庫的時候 預設這個方法的符号是被隐藏的
這樣做應該是為了除了這個apk外不讓其他程式使用這個庫的這個方法
 Java_com_sliver_cx_android_1test_MainActivity_stringFromJNI
jni方法名:命名格式 包名_方法名  anroid_1test這個數字似乎是ide自己加上的 實際上在調用FindClass尋找class時,不應該帶有這個
 JNIEnv *env,jobject
JNI方法的預設參數 env是JNI的運作環境 jobject是調用這個native方法的Java類
           

Java簽名

Java方法是帶有簽名的,而C/C++ native方法要調用Java方法 需要知道Java方法的簽名.
在terminal下:
首先編譯出class檔案 
javac test.java
執行 javap -s test可以輸出類test中方法的簽名

簽名對應規則 void call(); 對應 ()V
括号内是參數簽名 括号後是傳回值簽名 V對應void

引申出的多個參數簽名 void call(int a); 對應 (I)V
                  int  call(int a, char b);對應 (IC)I
           

JNI api

FindClass()
函數原型
jclass FindClass(const char* name)
{ 
    return functions->FindClass(this, name);
}
FindClass用來尋找在JNI環境中的Java Class 
name為指定的包名
例如 FindClass("com/cx/test/Test"); // com/cx/test是包名 Test是類名

 GetMethod
函數原型
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
    { return functions->GetMethodID(this, clazz, name, sig); }
GetMethod用來尋找JNI環境中的java類的方法
clazz為調用這個native方法的Java類
name為尋找的Java方法名
sig為Java方法的簽名
例如 GetMethod(obj, "call", "()V");
           

應用舉例

Java class:
public class Test {
    public void call(int nb) {
        System.out.println("cnt" + nb);
    }
    public native test_call();
}

調用 Java Class:
public class Call {
    public static void main(String[] args)  {
        Test a = new Test();
        a.test_call();
    }
}

JNI cpp:
extern "C"
JNIEXPORT void JNICALL
Java_com_cx_test_Test_call(
        JNIEnv *env,
        jobject obj,
        int cnt) {
        jclass myclass = env->FindClass("com/cx/test/Test");
        if (myclass == nullptr) {
            LOGI("failed to find class");
            return;
        }

        jmethodID myid = env->GetMethodID(myclass, "call", "(I)V");
        if (myid == nullptr) {
            LOGI("failed to get method id");
            return;
        }

        env->CallVoidMethod(obj, myid, );
}