使用JNI的原則
總之,只有當你必須在同一進程中調用本地代碼時,再使用JNI。
1、JAVA程序和本地程序使用TCP/IP或者IPC進行交互。
2、當用JAVA程序連接本地數據庫時,使用JDBC提供的API。
3、JAVA程序可以使用分佈式對象技術,如JAVA IDL API。
JNIEnv
JNIEnv 概念 : 是一個線程相關的結構體, 該結構體代表了 Java 在本線程的運行環境 ;
JNIEnv 與 JavaVM :
注意區分這兩個概念;
– JavaVM : JavaVM 是 Java虛擬機在 JNI 層的代表, JNI 全局只有一個;
– JNIEnv : JavaVM 在線程中的代表, 每個線程都有一個, JNI 中可能有很多個JNIEnv;
JNIEnv 作用 :
– 調用 Java 函數 : JNIEnv 代表 Java 運行環境, 可以使用 JNIEnv 調用 Java 中的代碼;
– 操作 Java 對象 : Java 對象傳入 JNI 層就是 Jobject 對象, 需要使用 JNIEnv 來操作這個 Java 對象;
JNIEnv 體系結構
線程相關 : JNIEnv 是線程相關的, 即 在 每個線程中 都有一個 JNIEnv 指針, 每個JNIEnv 都是線程專有的, 其它線程不能使用本線程中的 JNIEnv, 線程 A 不能調用 線程 B 的 JNIEnv;
JNI 例子:
- Java 層
private native String printJNI(String inputStr);
- 生成文件:com_android_HelloWorld.h
到classes一級目錄:/HelloWorld/bin/classes/
執行如下命令:
javah com.android.HelloWorld
生成文件:com_android_HelloWorld.h
- 實現JNI原生函數源文件:
新建com_android_HelloWorld.c文件:
定義OnLoad
OnLoadJava_com_android_HelloWorld_printJNI
編譯生成so庫
編譯com_android_HelloWorld.c成so庫可以和app一起編譯,也可以都單獨編譯。
參數轉換
1. 如何將java傳入的String參數轉換爲c的char*,然後使用?
java傳入的String參數,在c文件中被jni轉換爲jstring的數據類型,
//在c文件中聲明
char* test;
test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);
//注意:test使用完後,通知虛擬機平臺相關代碼無需再訪問:
(*env)->ReleaseStringUTFChars(env, jstring, test);
2.將c中獲取的一個char*的buffer傳遞給java?
這個char*如果是一般的字符串的話,作爲string傳回去就可以了。如果是含有’\0’的buffer,最好作爲bytearray傳出,因爲可以制定copy的length,如果copy到string,可能到’\0’就截斷了。
unsigned char buffer[256];
int i=0;
for(i=0;i<256;i++)
buffer[i] = i;
//new一個byte數組
jbyteArray array = (*env)->NewByteArray(env,256);
//拷貝
(*env)->SetByteArrayRegion(env, array, 0, 256, buffer);
3. char* 和jstring互轉
GetStringUTFChars
NewStringUTF
JNIEXPORT jstring JNICALL Java_com_explorer_jni_SambaTreeNative_getDetailsBy
(JNIEnv *env, jobject jobj, jstring pc_server, jstring server_user, jstring server_passwd)
{
const char *pc = env->GetStringUTFChars(pc_server, NULL);
const char *user = env->GetStringUTFChars(server_user, NULL);
const char *passwd = env->GetStringUTFChars(server_passwd, NULL);
const char *details = smbtree::getPara(pc, user, passwd);
jstring jDetails = env->NewStringUTF(details);
return jDetails;
}
4. 傳對象到JNI調用
jclass stu_cls = env->GetObjectClass(obj_stu); //或得Student類引用
jfieldID ageFieldID = env->GetFieldID(stucls,"age","I"); //獲得得Student類的屬性id
jfieldID nameFieldID = env->GetFieldID(stucls,"name","Ljava/lang/String;"); // 獲得屬性ID
jint age = env->GetIntField(objstu , ageFieldID); //獲得屬性值
jstring name = (jstring)env->GetObjectField(objstu , nameFieldID);//獲得屬性值
const char * c_name = env->GetStringUTFChars(name ,NULL);//轉換成 char *
string str_name = c_name ;
env->ReleaseStringUTFChars(name,c_name); //釋放引用
5. 回調Java層方法
jstring str = NULL;
jclass clz = env->FindClass("cc/androidos/jni/JniTest");
//獲取clz的構造函數並生成一個對象
jmethodID ctor = env->GetMethodID(clz, "<init>", "()V");
jobject obj = env->NewObject(clz, ctor);
// 如果是數組類型,則在類型前加[,如整形數組int[] intArray,則對應類型爲[I,整形數組String[] strArray對應爲[Ljava/lang/String;
jmethodID mid = env->GetMethodID(clz, "sayHelloFromJava", "(Ljava/lang/String;II[I)I");
jint javaIndex = env->CallIntMethod(obj, mid, str1, index1, index2, testIntArray);
Java 類型 | 本地類型 | 描述 |
---|---|---|
boolean | jboolean | C/C++8位整型 |
byte | jbyte | C/C++帶符號的8位整型 |
char | jchar | C/C++無符號的16位整型 (char 在java中是2個字節。java採用unicode,2個字節(16位)來表示一個字符) |
short | jshort | C/C++帶符號的16位整型 |
int | jint | C/C++帶符號的32位整型 |
long | jlong | C/C++帶符號的64位整型e |
float | jfloat | C/C++32位浮點型 |
double | jdouble | C/C++64位浮點型 |
Object | jobject | 任何Java對象,或者沒有對應java類型的對象 |
Class | jclass | Class對象 |
String | jstring | 字符串對象 |
Object[] | jobjectArray | 任何對象的數組 |
boolean[] | jbooleanArray | 布爾型數組 |
byte[] | jbyteArray | 比特型數組 |
char[] | jcharArray | 字符型數組 |
short[] | jshortArray | 短整型數組 |
int[] | jintArray | 整型數組 |
long[] | jlongArray | 長整型數組 |
float[] | jfloatArray | 浮點型數組 |
double[] | jdoubleArray | 雙浮點型數組 |
Java 類型 | 符號 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
objects對象 | L類名 |
Arrays數組 | [數組類型 |
methods方法 | (參數類型)返回類型 |
String | Ljava/lang/String; |
int[ ] | [I |
float[ ] | [F |
Object[ ] | [Ljava/lang/Object; |
int [ ][ ] | 其描述符爲[[I |
float[ ][ ] | 其描述符爲[[F |
Java層方法 | JNI函數簽名 |
---|---|
String test ( ) | Ljava/lang/String; |
int f (int i, Object object) | (ILjava/lang/Object;)I |
void set (byte[ ] bytes) | ([B)V |