Android JNI開發(fā)
什么是JNI?有什么用?
JNI是Java Native Interface的縮寫,它提供了若干的API實(shí)現(xiàn)了Java和其他語言的通信(主要是C&C++)
JNI一般創(chuàng)建流程
方法一
- java代碼中創(chuàng)建本地方法使用
native關(guān)鍵字標(biāo)識 如:
public native void stringFromJNI();
public native void stringFromJNI2();
public native String stringFromJNI3(String s);
- 在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)
- java層
public native String _test();
- 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的反射
- FindClass 可以在c后c++中找到j(luò)ava中的類
- GetMethodID/GetFieldID 拿到方法和屬性的索引值
- NewObject 獲取對象
- 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 ;
}