Android JNI開發(fā)

Android JNI開發(fā)

什么是JNI?有什么用?

JNI是Java Native Interface的縮寫,它提供了若干的API實(shí)現(xiàn)了Java和其他語言的通信(主要是C&C++)

JNI一般創(chuàng)建流程

方法一

  1. java代碼中創(chuàng)建本地方法使用 native 關(guān)鍵字標(biāo)識 如:
public native void stringFromJNI();
public native void stringFromJNI2();
public native String stringFromJNI3(String s);
  1. 在c中編寫對應(yīng)方法如:
extern "C"
JNIEXPORT void JNICALL
Java_com_boby_openslaudio_MainActivity_stringFromJNI1(JNIEnv *env, jobject thiz) { 
    using namespace std;
    cout << "Hello___________World";
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_boby_openslaudio_MainActivity_stringFromJNI2(JNIEnv *env, jobject thiz) { 
    return env->NewStringUTF("hello word !");
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_boby_openslaudio_MainActivity_stringFromJNI3(JNIEnv *env, jobject thiz, jstring s) {
    const  char *str=env->GetStringUTFChars(test,0);
    env->ReleaseStringUTFChars(test,str);
    return env->NewStringUTF("aaa");
}
  • extern "C" : 確保c++編譯器在調(diào)用c代碼中的函數(shù)時使用未經(jīng)修改的名稱
  • JNIEXPORT : 標(biāo)識后面接著實(shí)際jni返回的值,對應(yīng)java方法中的返回值 如 void 、 jstring 、jint 等
  • JNICALL :標(biāo)識后面接jni調(diào)用的c方法,方法名規(guī)律:JAVA_包名_類名_方法名
  • JNIEnv * env:這個env可以看做是Jni接口本身的一個對象,jni.h頭文件中存在著大量被封裝好的函數(shù),這些函數(shù)也是Jni編程中經(jīng)常被使用到的,要想調(diào)用這些函數(shù)就需要使用JNIEnv這個對象。例如:env->GetObjectClass()。(詳情請查看jni.h)
  • jobject obj:代表著native方法的調(diào)用者本身,如: new NativeDemo();但如果native是靜態(tài)的,那就是NativeDemo.class
  • 如果java中需要傳參給c,前兩個參數(shù)固定為 JNIEnv * env ,jobject obj 第三個參數(shù)開始則為傳遞的參數(shù)。

JVM將JNI接口指針傳遞給本地方法,本地方法只能在當(dāng)前線程中訪問該接口指針,不能將接口指針傳遞給其它線程使用。在VM中 JNI接口指針指向的區(qū)域用來分配和存儲線程本地數(shù)據(jù)。

當(dāng)Java代碼調(diào)用本地方法時,VM將JNI接口指針作為參數(shù)傳遞給本地方法,當(dāng)同一個Java線程調(diào)用本地方法時VM保證傳遞給本地方法的參數(shù)是相同的。不過,不同的Java線程調(diào)用本地方法時,本地方法接收到的JNI接口指針是不同的。

基本類型

Java Language Type JNI Type
boolean jboolean
byte jbyte
char jchar
short jshort
int jint
long jlong
float jfloat
double jdouble
All Reference type jobject

方法二

在 JNI_OnLoad中調(diào)用

registerNative 手動映射java方法

typedef struct{
    const char* name; //java層定義的名字
    const char* signature;//方法簽名,有規(guī)格的字符串,標(biāo)注輸入和輸出參數(shù)
    void* fnPtr;//真正需要映射到c的具體的Api
}JNINativeMethod;

注冊Native的最佳時期

  • jint JNI_OnLoad(JavaVM *vm, void *reserved )
  • Jint JNI_OnUnload(JavaVM *vm, void *reserved)
  1. java層
public native String  _test();
  1. c++層
#define JNI_CLASS_PATH "com/boby/openslaudio/MainActivity"

extern "C"
JNIEXPORT void JNICALL
my_test_register(JNIEnv *env, jobject thiz) { 
   retrun env->NewStringUTF("this is a test of register")
}
//方法映射表
static JNINativeMethod g_methods[] ={
        "_test","()Ljava/lang/String;",(void*)my_test_register
};

jint JNI_OnLoad(JavaVM *vm,void *reserved){//同一個進(jìn)程只有一個JavaVM
     JNIEnv *env = NULL;
    vm->GetEnv((void ** )&env,JNI_VERSION_1_6);
     jclass clazz = env->FindClass(JNI_CLASS_PATH);
        //注冊本地方法 參數(shù):class,方法映射表,方法映射表中的個數(shù)
    env->RegisterNatives(clazz,g_methods, sizeof(g_methods)/ sizeof(g_methods[0]));
    return JNI_VERSION_1_6;
}

JNI中的Signature

java與c/c++相互調(diào)用時,用于描述函數(shù)參數(shù)的描述符

可以簡單理解為java 虛擬機(jī)中有一個映射表。函數(shù)名和參數(shù)拼接在一起形成一個唯一的key ,通過key可以找到函數(shù)指針

  • 輸入?yún)?shù)放在( )內(nèi),輸出參數(shù)放在( )外的右邊
  • 多個參數(shù)之間順序存放,用;分割

原始類型的Signature

java類型 符號
boolean Z
byte B
char C
short S
int I
long L
float F
double D
void V

類的Signature

  • java對象 “L包路徑/類名” 如:Ljava/lang/String

  • java數(shù)組 “ [ ”

    例:

  • ([LStudent;])[LStudenty; --> Student[] Xxx(student[] s)

    • ([Ljava/lang/String;)[Ljava/lang/Object; --> Object[] Xxx(String[] s)
    • ([I;[Ljava/lang/String;LStudent;)[Ljava/lang/Object --> Object[] Xxx(int [] a,String[] b,Student c)

C/C++ 調(diào)Java方法

調(diào)用過程有點(diǎn)像java的反射

  1. FindClass 可以在c后c++中找到j(luò)ava中的類
  2. GetMethodID/GetFieldID 拿到方法和屬性的索引值
  3. NewObject 獲取對象
  4. Call<TYPE>Method/[G/S]et<Type>Field 調(diào)用方法或調(diào)用屬性

java

public class Student {
    private String name;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
  //測試java調(diào)用
  public native String test_c_callJava();

extern "C"
JNIEXPORT jstring JNICALL
test_c_calljava(JNIEnv *env, jobject thiz) {
    //1.找到class
    jclass clazz=env->FindClass("com/boby/openslaudio/Student");
    // 2.找到屬性/方法的索引值
    jmethodID  jmethod_init_id =env->GetMethodID(clazz,"<init>","()V");
    jmethodID  jmethod_set_id =env->GetMethodID(clazz,"setName","(Ljava/lang/String;)V");
    jmethodID  jmethod_get_id =env->GetMethodID(clazz,"getName","()Ljava/lang/String;");
    // 3.創(chuàng)建對象
    jobject  obj=env->NewObject(clazz,jmethod_init_id);
    //4.調(diào)用對應(yīng)的方法
    env->CallVoidMethod(obj,jmethod_set_id,env->NewStringUTF("boby"));
    jstring name= (jstring)env->CallObjectMethod(obj, jmethod_get_id);
    return name ;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容