android jni程式碼編寫規則--整理總結
一. java引數型別和jni本地引數型別對照
基本型別
Java 型別 jni本地型別 描述
boolean jboolean C/C++ unsigned 8 bits
byte jbyte C/C++ signed 8 bits
char jchar C/C++ unsigned 16 bits
short jshort C/C++ signed 16 bits
int jint C/C++ signed 32 bits
long jlong C/C++ signed 64 bits
float jfloat C/C++ 32位浮點型
double jdouble C/C++ 64位浮點型
void void N/A
表一
物件型別
Object jobject 任何Java物件,或者沒有對應
java型別的物件
Class jclass class物件
String jstring 字串物件
表二
陣列型別
boolean[] jbooleanArray 布林型陣列 unsigned
byte[] jbyteArray 位元型陣列 signed
char[] jcharArray 字元型陣列 unsigned
short[] jshortArray 短整型陣列 signed
int[] jintArray 整型陣列 signed
long[] jlongArray 長整型陣列 signed
float[] jfloatArray 浮點型陣列
double[] jdoubleArray 雙浮點型陣列
Object[] jobjectArray 任何物件的陣列
表三
JNI引用型別與Java的對應關係如下樹層次圖:
1. java中的返回值void和JNI中的void是完全對應的。
2. java中的基本資料型別(boolean ,byte , char ,short ,int,long,float,double八種)在JNI中對應的資料型別只要在前面加上j就對應了(jboolean ,jbyte , jchar ,jshort ,jint,jlong,jfloat,jdouble)。
JNI中還有個Java中沒有的jsize,定義如下:
typedef jint jsize;
其實jsize整型是用來描述基本指標和大小。
3. java中的物件,包括類庫中定義的類、介面以及自定義的類介面,都對應於JNI中的jobject。
4. java中基本資料型別的陣列對應與JNI中的j<type>array型別。(type就是上面說的8種基本資料型別)
5. java中物件的陣列對應於JNI中的jobjectArray型別。(在java中一切物件、介面以及陣列都是物件)
http://blog.csdn.net/xyz_lmn/article/details/6956003
http://www.cnblogs.com/liangwind/archive/2009/08/18/1925515.html
Java基本型別的精度
java 的基本資料型別是不存在有符號和無符號這種概念的. JAVA中的基本資料型別不存在無符號的,它們的取值範圍是固定的,不會隨著機器硬體環境或者作業系統的改變而改變。
簡單型別 位元組數 範圍/精度
float 4 32位IEEE754單精度
double 8 64位IEEE754雙精度
byte 1 -128到127
short 2 -32,768到32,767
int 4 -2,147,483,648到2,147,483,647
long 8 -9,223,372,036,854,775,808到9,223,372,036,854,775,807
char 2 整個Unicode字符集
boolean 1 True或者false
像byte 是範圍是 -128到127, 你想要變為 0到255 怎麼辦, 跟 0XFF 做與運算 就可以了.
如 byte bb , 如果你想賦值它值 255, 那是不行的, 就算賦值了, bb 的值也是 255 對 256 求模後的值 -1
如果你只是想取他 0到255 的值, 還是很簡單的,
bb & 0XFF , 如 bb = -1, 那 bb & 0XFF 結果為 255,
這個與運算後的結果會隱式轉換為int 型別的, 因為 byte 放不下了.
與運算 還是很快的, 比加減法還快的.
http://www.stuhack.com/biancheng/java/35169.html
二.jni層使用java的基本型別資料
對於上面八種基本的資料型別boolean ,byte , char ,short ,int,long,float,double,jni層的c++程式碼可以用強制直接轉換成對於長度的c/c++型別資料。
如:unsigned char tmp = (unsigned char) m_jboolean;
unsigned short tmp = (unsigned short)m_jchar;
或者同長度型別的資料就可以直接賦值的,int tmp = m_jint;
三.jni層對陣列的使用
JNI通過JNIEnv提供的操作Java陣列的功能。它提供了兩個函式:一個是操作java的簡單型陣列的,另一個是操作物件型別陣列的。
1. 操作java的簡單型陣列
因為速度的原因,簡單型別的陣列作為指向本地型別的指標暴露給原生程式碼。因此,它們能作為常規的陣列存取。這個指標是指向實際的Java陣列或者Java陣列的拷貝的指標。另外,陣列的佈置保證匹配本地型別。
為了存取Java簡單型別的陣列,你就要要使用GetXXXArrayElements函式(見表三),XXX代表了陣列的型別。這個函式把Java陣列看成引數,返回一個指向對應的本地型別的陣列的指標。
完整的函式族見下表:
函式 Java 陣列型別 本地型別
GetBooleanArrayElements jbooleanArray jboolean
GetByteArrayElements jbyteArray jbyte
GetCharArrayElements jcharArray jchar
GetShortArrayElements jshortArray jshort
GetIntArrayElements jintArray jint
GetLongArrayElements jlongArray jlong
GetFloatArrayElements jfloatArray jfloat
GetDoubleArrayElements jdoubleArray jdouble
當你對陣列的存取完成後,要確保呼叫相應的ReleaseXXXArrayElements函式,引數是對應Java陣列和GetXXXArrayElements返回的指標。如果必要的話,這個釋放函式會複製你做的任何變化(這樣它們就反射到java陣列),然後釋放所有相 關的資源。
例如:
static jint com_ginwave_fs_com_HWRC_GetRecogRange(JNIEnv* env, jclass clazz, jintArray Handle)
{
unsigned long *pHandle = NULL;
int ret = 0;
jint *tmpHandle = env->GetIntArrayElements(Handle, 0);
pHandle = (unsigned long *)tmpHandle;
ret = (int)HWRC_GetRecogRange(pHandle);
env->ReleaseIntArrayElements(Handle, tmpHandle, 0);
return r
}
獲取陣列的長度:
jint theArrayLength = env->GetArrayLength(Frame);
建立一個新的函式陣列簇如下:
NewBooleanArray
NewByteArray
NewCharArray
NewShortArray
NewIntArray
NewLongArray
NewFloatArray
NewDoubleArray
引數為陣列長度,如:
jbyte *list;
jbyteArray byteArray = NULL;
byteArray = env->NewByteArray(len);
if (byteArray)
env->SetByteArrayRegion(byteArray, 0, len, list);
關於函式簇GetXXXArrayRegion和SetXXXArrayRegion,其中XXX為基本型別。
例如:
env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);
Setxxx的方向是從JNI層往java層傳遞;
env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset);
而Getxxx的方向則是資料從java層向jni層傳遞。
這裡是獲取簡單型陣列中的資料供jni或者下層使用,如果需要在jni層設定java
中對於的簡單型陣列的話,就需要使用到接下來講到的物件型別的一些操作。
總結下,有以下幾簇函式:
GetArrayLength
NewXXXArray
GetXXXArrayElements
ReleaseXXXArrayElements
GetXXXArrayRegion
SetXXXArrayRegion
對於資料,暫時遇到這些函式了。。。
2. 操作java物件型別資料
Java物件做為引用被傳遞到本地方法中,所有這些Java物件的引用都有一個共同的父型別jobject(相當於java中的Object類是所有類的父類一樣)。
1). string物件
從java程式中傳過去的String物件在本地方法中對應的是jstring型別,jstring型別和c中的char*不同,所以如果你直接當做char*使用的話,就會出錯。因此在使用之前需要將jstring轉換成為c/c++中的char*,這裡使用JNIEnv的方法轉換。
static jstring com_prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const char *str = (*env)->GetStringUTFChars(env, prompt, 0);
printf("%s", str);
env->ReleaseStringUTFChars(prompt, str);
...
}
這裡使用GetStringUTFChars方法將傳進來的prompt(jstring型別)轉換成為UTF-8的格式,就能夠在本地方法中使用了。
注意:在使用完你所轉換之後的物件之後,需要顯示呼叫ReleaseStringUTFChars方法,讓JVM釋放轉換成UTF-8的string的物件的空間,如果不顯示的呼叫的話,JVM中會一直儲存該物件,不會被垃圾回收器回收,因此就會導致記憶體溢位。
下面是訪問String的一些方法:
GetStringUTFChars 將jstring轉換成為UTF-8格式的char*
GetStringChars 將jstring轉換成為Unicode格式的char*
ReleaseStringUTFChars 釋放指向UTF-8格式的char*的指標
ReleaseStringChars 釋放指向Unicode格式的char*的指標
NewStringUTF 建立一個UTF-8格式的String物件
NewString 建立一個Unicode格式的String物件
GetStringUTFLength 獲取UTF-8格式的char*的長度
GetStringLength 獲取Unicode格式的char*的長度
提供給兩個jstring和char *互相轉換的函式:
/* c/c++ string turn to java jstring */
static jstring strTojstring(JNIEnv* env, const unsigned char* pStr)
{
int strLen = strlen((const char*)pStr);
jclass jstrObj = env->FindClass("java/lang/String");
jmethodID methodId = env->GetMethodID(jstrObj, "", "([BLjava/lang/String;)V");
jbyteArray byteArray = env->NewByteArray(strLen);
jstring encode = env->NewStringUTF("utf-8");
env->SetByteArrayRegion(byteArray, 0, strLen, (jbyte*)pStr);
return (jstring)env->NewObject(jstrObj, methodId, byteArray, encode);
}
//check ok!
/* java jstring turn to c/c++ string */
static char* jstringTostr(JNIEnv* env, jstring jstr)
{
char* pStr = NULL;
jclass jstrObj = env->FindClass("java/lang/String");
jstring encode = env->NewStringUTF("utf-8");
jmethodID methodId = env->GetMethodID(jstrObj, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray byteArray = (jbyteArray)env->CallObjectMethod(jstr, methodId, encode);
jsize strLen = env->GetArrayLength(byteArray);
jbyte *jBuf = env->GetByteArrayElements(byteArray, JNI_FALSE);
if (jBuf > 0)
{
pStr = (char*)malloc(strLen + 1);
if (!pStr)
{
return NULL;
}
memcpy(pStr, jBuf, strLen);
pStr[strLen] = 0;
}
env->ReleaseByteArrayElements(byteArray, jBuf, 0);
return pStr;
}
// check ok!
2) 訪問java物件
JNI提供的另外一個功能是在原生程式碼中使用Java物件。通過使用合適的JNI函式,你可以建立Java物件,get、set 靜態(static)和 例項(instance)的域,呼叫靜態(static)和例項(instance)函式。JNI通過ID識別域和方法,一個域或方法的ID是任何處理域和方法的函式的必須引數。
下表列出了用以得到靜態(static)和例項(instance)的域與方法的JNI函式。每個函式接受(作為引數)域或方法的類,它們的名稱,符號和它們對應返回的jfieldID或jmethodID。
函式 描述
GetFieldID 得到一個例項的域的ID
GetStaticFieldID 得到一個靜態的域的ID
GetMethodID 得到一個例項的方法的ID
GetStaticMethodID 得到一個靜態方法的ID
下面以一個例子來說明用法:上下層之間需要傳遞一個或者多個結構體值。
c/c++結構體定義:
typedef struct tagTHWFrame{
short left;
short top;
short width;
short height;
} THWFrame;
當然在java層也需要定義一個匹配的類出來:
public class THWFrame{
public short left;
public short top;
public short width;
public short height;
}
注意貌似這裡只能定義成public的。
下面是jni層相關的程式碼,主要思想是對java對應類物件的屬性域獲得ID值後一個一個訪問。
/* int HWRC_SetInputBox( unsigned long *pHandle, const THWFrame *pFrame ); */
static void ObjectTOTHWFrameStruct(JNIEnv* env, jobjectArray Frame, THWFrame *pFrame, int index)
{
jobject obj = env->GetObjectArrayElement(Frame, index);
jclass cls = env->GetObjectClass(obj);
jfieldID left = env->GetFieldID(cls, "left", "S");
pFrame[index].left = (short)env->GetShortField(obj, left);
jfieldID top = env->GetFieldID(cls, "top", "S");
pFrame[index].top = (short)env->GetShortField(obj, top);
jfieldID width = env->GetFieldID(cls, "width", "S");
pFrame[index].width = (short)env->GetShortField(obj, width);
jfieldID height = env->GetFieldID(cls, "height", "S");
pFrame[index].height = (short)env->GetShortField(obj, height);
}
static jint com_ginwave_fs_com_HWRC_SetInputBox(JNIEnv* env, jclass clazz,
jintArray Handle, jobjectArray Frame)
{
unsigned long *pHandle = NULL;
THWFrame *pFrame = NULL;
int frame_len = 0;
int ret = 0;
jint *tmpHandle = env->GetIntArrayElements(Handle, 0);
pHandle = (unsigned long *)tmpHandle;
jint theArrayLength = env->GetArrayLength(Frame);
frame_len = theArrayLength;
pFrame = (THWFrame *)malloc( sizeof(THWFrame) * frame_len );
for( int i = 0; i < frame_len; i++ ){
ObjectTOTHWFrameStruct(env, Frame, pFrame, i);
}
ret = HWRC_SetInputBox(pHandle, (const THWFrame *)pFrame);
env->ReleaseIntArrayElements(Handle, tmpHandle, 0);
free(pFrame);
frame_len = NULL;
return ret;
}
// {"HWRC_SetInputBox", "([I[Ljava/com/ginwave/fs/com/THWFrame)I" , (void *)com_ginwave_fs_com_HWRC_SetInputBox },
// check ok!
/* int HWRC_GetInputBox( unsigned long *pHandle, THWFrame *pFrame ); */
static void THWFrameStructTOObject(JNIEnv* env, jobjectArray Frame, THWFrame *pFrame, int index)
{
jobject obj = env->GetObjectArrayElement(Frame, index);
jclass cls = env->GetObjectClass(obj);
jfieldID left = env->GetFieldID(cls, "left", "S");
env->SetShortField(obj, left, (short)pFrame[index].left);
jfieldID top = env->GetFieldID(cls, "top", "S");
env->SetShortField(obj, top, (short)pFrame[index].top);
jfieldID width = env->GetFieldID(cls, "width", "S");
env->SetShortField(obj, width, (short)pFrame[index].width);
jfieldID height = env->GetFieldID(cls, "height", "S");
env->SetShortField(obj, height, (short)pFrame[index].height);
}
static jint com_ginwave_fs_com_HWRC_GetInputBox(JNIEnv* env, jclass clazz,
jintArray Handle, jobjectArray Frame)
{
unsigned long *pHandle = NULL;
THWFrame *pFrame = NULL;
int frame_len = 0;
int ret = 0;
jint *tmpHandle = env->GetIntArrayElements(Handle, 0);
pHandle = (unsigned long *)tmpHandle;
jint theArrayLength = env->GetArrayLength(Frame);
frame_len = theArrayLength;
pFrame = (THWFrame *)malloc( sizeof(THWFrame) * frame_len );
ret = HWRC_GetInputBox(pHandle, pFrame);
for( int i = 0; i < frame_len; i++ ){
THWFrameStructTOObject(env, Frame, pFrame, i);
}
env->ReleaseIntArrayElements(Handle, tmpHandle, 0);
free(pFrame);
frame_len = NULL;
return ret;
}
// {"HWRC_GetInputBox", "([I[Ljava/com/ginwave/fs/com/THWFrame)I" , (void *)com_ginwave_fs_com_HWRC_GetInputBox },
// check ok!
其中,比較難理解的應該是函式的簽名了,下面是他們的一些規則:
這個陣列的型別是JNINativeMethod,定義如下:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一個變數name是Java中函式的名字。
第二個變數signature,用字串是描述了函式的引數和返回值
第三個變數fnPtr是函式指標,指向C函式。
其中比較難以理解的是第二個引數,例如
"()V"
"(II)V"
"(Ljava/lang/String;Ljava/lang/String;)V"
實際上這些字元是與函式的引數型別一一對應的。
"()" 中的字元表示引數,後面的則代表返回值。例如"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);
具體的每一個字元的對應關係如下
字元 Java型別 C型別
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
陣列則以"["開始,用兩個字元表示
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]
objects物件 Lfully-qualified-class-name; L類名
Arrays陣列 [array-type [陣列型別
方法引數或者返回值為java中的物件時,必須以“L”加上其路徑,不過此路徑必須以“/”分開,自定義的物件也使用本規則,不在包中時直接“L”加上類名稱。比如說 java.lang.String為“java/lang/String”,com.nedu.jni.helloword.Student為"com /nedu/jni/helloword/Student"
方法引數或者返回值為陣列時型別前加上[,例如[I表示 int[],[[[D表示 double[][][],即幾維陣列就加幾個[。
JNI函式中始終包含兩個必要的引數:JNIEnv* env, jclass clazz
JNIEnv *――它是一個介面指標,用於定位函式表中的函式!
在JNI規範中一般稱 為 “Interface Pointer”。看到這兒好像和過程呼叫很類似了!是的,JNI中的操作過程,就是程式導向的!後面的jobject是 一個指向該類的指標,類似與C語言中的this。這個第二個引數是變化的,當該方法為類的例項方法時該引數為jobject;當該方法為類方法 (即靜態方法)時該引數為jclass,指向該類的class。
通過ndk程式設計來得到jni層標頭檔案的時候,這第二個引數對於staic方法,生成出來的就是jclass,而對於非staic方法,生成出來的就是jobject。
從上圖可知,jobject包含了其實概括了所有的java型別,也就是說,像上圖中的非jobject型別的資料,在傳遞引數的時候都可以以jobject型別傳遞下去。比如說,如果要java中要傳遞一個二(多)維int陣列下去,就可以包裝成jobjectArray傳下去,只不過對應的簽名要弄成[[I了。
對於訪問java物件的方法
在本地方法中呼叫Java物件的方法的步驟:
①.獲取你需要訪問的Java物件的類:
jclass cls = (*env)->GetObjectClass(env, obj); // FindClass(“android/util/log”)
使用GetObjectClass方法獲取obj對應的jclass。 // 直接搜尋類名,需要是static修飾的類。
②.獲取MethodID:
jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V");
// GetStaticMethodID(…) , 獲取靜態方法的ID
使用GetMethdoID方法獲取你要使用的方法的MethdoID。其引數的意義:
env-->JNIEnv
cls-->第一步獲取的jclass
"callback"-->要呼叫的方法名
"(I)V"-->方法的Signature, 簽名同前面的JNI規則。
③.呼叫方法:
(*env)->CallVoidMethod(env, obj, mid, depth);
// CallStaticIntMethod(….) , 呼叫靜態方法
使用CallVoidMethod方法呼叫方法。引數的意義:
env-->JNIEnv
obj-->通過本地方法穿過來的jobject
mid-->要呼叫的MethodID(即第二步獲得的MethodID)
depth-->方法需要的引數(對應方法的需求,新增相應的引數)
注:這裡使用的是CallVoidMethod方法呼叫,因為沒有返回值,如果有返回值的話使用對應的方法,在後面會提到。
CallVoidMethod CallStaticVoidMethod
CallIntMethod CallStaticVoidMethod
CallBooleanMethod CallStaticVoidMethod
CallByteMethod CallStaticVoidMethod
…
其實jni中還有很多很多的介面函式這裡沒有列舉,可以直接參考原始碼:
$ find frameworks/base type d -name jni
./voip/jni
./rfid/jni
./freestylus/jni
./native/graphics/jni
./drm/jni
./tests/BrowserTestPlugin/jni
./services/jni
./packages/TtsService/jni
./media/jni
./media/libdrm/mobile1/include/jni
./media/libdrm/mobile1/src/jni
./graphics/jni
./core/jni
./opengl/tests/gl_jni/jni
./opengl/tests/gl2_jni/jni
./opengl/tests/gldual/jni
這麼多jni目錄都可以參考,其中主要是core/jni目錄了。
四、關於異常
異常介面有:
jniThrowException(env, "java/lang/RuntimeException", "Method called after release()");
env->ThrowNew(env->FindClass("java/io/IOException"),"CWJLog Error, IOException");
doThrow(env, "java/lang/IllegalStateException", msg);
使用Throw,自己構造(沒用過)
jclass clazz = env->FindClass("java/io/IOException");
jmethodID methodId = env->GetMethodID(clazz, "<init>", "()V");
jthrowable throwable = env->NewObject(clazz, methodId);
env->Throwthrowable);
參考網址:
http://blog.csdn.net/xyz_lmn/article/details/6959545
Android JNI入門第三篇——jni標頭檔案分析
http://blog.csdn.net/xyz_lmn/article/details/6966259
Android JNI入門第四篇——Android.mk檔案分析
http://blog.csdn.net/xyz_lmn/article/details/7017420
Android JNI開發提高篇
http://blog.csdn.net/xyz_lmn/article/details/6956003
Android JNI入門第二篇——Java引數型別與本地引數型別對照
http://wenku.baidu.com/view/e9e28ca1b0717fd5360cdc18.html
JNI入門
http://www.ibm.com/developerworks/cn/java/j-jni/
使用 Java Native Interface 的最佳實踐
http://helloxuweifu.iteye.com/blog/1168647
http://blog.csdn.net/kangyaping/article/details/6584027
JNI函式呼叫大全
http://newfaction.net/2010/11/30/java-jni-getfieldid-and-getmethodid-and-parameter-description.html
java jni GetFieldID 和 GetMethodID 以及引數的說明
http://hi.baidu.com/spmno/blog/item/7d4d764ea78a6809b3de0588.html
jni中使用陣列的幾個方法
http://xxw8393.blog.163.com/blog/static/3725683420107109411366/
JNI 返回結構體引數
http://www.cnblogs.com/nicholas_f/archive/2010/11/30/1892124.html
JNI中java型別與C/C++型別對應關係
http://blog.csdn.net/sunny09290/article/details/6884994
JNI資料型別
http://www.cnblogs.com/liangwind/archive/2009/08/18/1925515.html
jni --c/c++ 資料型別、陣列、物件
http://www.cnblogs.com/diyunpeng/archive/2009/09/24/1573296.html
Java有符號數與無符號數
http://www.stuhack.com/biancheng/java/35169.html
Java的基本資料型別是無符號的
相關文章
- Android原始碼編譯整理總結Android原始碼編譯
- PL/SQL 01 程式碼編寫規則SQL
- css書寫規則總結CSS
- 編寫和除錯Android下JNI程式流程除錯Android
- Java程式碼編寫、程式碼優化技巧總結Java優化
- 釣魚網站規則提取工作的python程式碼重寫總結網站Python
- Android 程式碼混淆規則Android
- 高效的jQuery程式碼編寫技巧總結jQuery
- 編寫程式碼的若干個基本規則(以Java為例)Java
- JavaScript-總結常用程式碼書寫規範JavaScript
- nginx配置location總結及rewrite規則寫法Nginx
- 程式碼規範之前端編寫碼規範前端
- 在 Android 中使用 JNI 的總結Android
- php的編碼規則PHP
- 編寫高效的Android程式碼Android
- css規則整理CSS
- Android JNI 程式碼自動生成Android
- Android程式碼的書寫規範Android
- 函式呼叫規則總結函式
- android 混淆規則作用,Android程式碼混淆詳解Android
- 程式碼規範整理
- 如何編寫高效的Android程式碼Android
- PL/SQL的編碼規則SQL
- 編碼規則指南(轉貼)
- JNI 學習總結
- Android原始碼編譯--整理Android原始碼編譯
- 個人總結的一些寫JS程式碼的基本規範JS
- 前端單體編碼規範整理前端
- 個人總結——全面的『Python編碼規範』Python
- Java原始碼的折行規則(編碼規範)Java原始碼
- 主資料之編碼規則
- android 編碼規範Android
- [轉]高質量JAVA程式碼編寫規範Java
- 編寫可閱讀的程式碼--基本規約
- 程式設計書寫規則 (轉)程式設計
- 我總結的Android程式設計規範Android程式設計
- 程式碼混淆的規則
- 密碼生成常見的編碼規則密碼