Android Studio NDK 入門教程(2)--Java與C++之間的簡單資料轉換與傳遞
概述
本文將講解Java與原生程式碼之間的資料轉換,包括基礎型別之間的轉換,以及陣列的傳遞與轉換。
型別轉換表
JAVA基礎型別與C++之間的對應表
Java型別 | C/C++型別 | 描述 |
---|---|---|
boolean | jboolean | 無符號8位整數 |
byte | jbyte | 有符號8位整數 |
char | jchar | 有符號16位整數 |
short | jshort | 有符號16位整數 |
int | jint | 有符號32位整數 |
long | jlong | 有符號64位整數 |
float | jfloat | 32位單精度浮點數 |
double | jdouble | 64位雙精度浮點數 |
這一點可以從jni.h標頭檔案中定義看到:
typedef unsigned char jboolean; /* unsigned 8 bits */
typedef signed char jbyte; /* signed 8 bits */
typedef unsigned short jchar; /* unsigned 16 bits */
typedef short jshort; /* signed 16 bits */
typedef int jint; /* signed 32 bits */
typedef long long jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
由上面的JNI定義程式碼可以看到,基礎型別都是根據Java型別大小等價轉換的。因此在使用Java傳遞過來的基礎型別或者返回Java中的基礎型別都可以直接使用。比如:
JNIEXPORT jint JNICALL
Java_com_example_wastrel_test_Test_BaseTypeTest(JNIEnv *env, jclass type,jint i) {
return i+5;
}
Java引用型別與C++之間的對應表
Java型別 | C/C++型別 | 描述 |
---|---|---|
Objec | jobject | 任何Java物件 |
Class | jclass | Class類物件 |
String | jstring | String類物件 |
Object[] | jobjectArray | 物件陣列 |
boolean[] | jbooleanArray | 布林陣列 |
byte[] | jbyteArray | 位元組陣列 |
char[] | jcharArray | 字元型陣列 |
short[] | jshortArray | 短整型陣列 |
int[] | jintArray | 整型陣列 |
long[] | jlongArray | 長整型陣列 |
float[] | jfloatArray | 浮點型陣列 |
double[] | jdoubleArray | 雙精度浮點型陣列 |
/*
* Reference types, in C++
*/
class _jobject {};
class _jclass : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jobjectArray : public _jarray {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jthrowable : public _jobject {};
typedef _jobject* jobject;
typedef _jclass* jclass;
typedef _jstring* jstring;
typedef _jarray* jarray;
typedef _jobjectArray* jobjectArray;
typedef _jbooleanArray* jbooleanArray;
typedef _jbyteArray* jbyteArray;
typedef _jcharArray* jcharArray;
typedef _jshortArray* jshortArray;
typedef _jintArray* jintArray;
typedef _jlongArray* jlongArray;
typedef _jfloatArray* jfloatArray;
typedef _jdoubleArray* jdoubleArray;
typedef _jthrowable* jthrowable;
typedef _jobject* jweak;
通過jni裡的定義可以看到,任何引用型別(陣列在Java中也是引用傳遞的)傳遞到C++裡面只是一個指向Java物件的指標。因此引用型別不能直接使用,JNIEnv提供了大量的方法來完成了他們之間的轉換。
使用JNIEnv完成資料之間的轉換
這裡只說明引用物件之間的轉換,因為對於普通型別,Java與C++是互通的。
String的傳遞
這裡將String單獨拿出來講解,因為String在Java中有著超高的使用頻次,JNIEnv對String有相對應的轉換函式。JNI裡的函式定義如下:
/**轉換成unicode相關函式 */
jstring NewString(const jchar*, jsize);
jsize GetStringLength(jstring);
const jchar* GetStringChars(jstring, jboolean* isCopy);
void ReleaseStringChars(jstring, const jchar*);
/** 轉換成UTF-8相關函式 */
jstring NewStringUTF(const char*);
jsize GetStringUTFLength(jstring);
const char* GetStringUTFChars(jstring, jboolean* isCopy);
void ReleaseStringUTFChars(jstring, const char*);
當從 JNI 函式 GetStringChars中返回得到字串B時,如果B是原始字串java.lang.String 的拷貝,則isCopy被賦值為JNI_TRUE。如果B和原始字串指向的是JVM中的同一份資料,則 isCopy被賦值為 JNI_FALSE。
當isCopy值為JNI_FALSE時,原生程式碼決不能修改字串的內容,否則JVM中的原始字串也會被修改,這會打破 JAVA語言中字串不可變的規則。
通常,因為你不必關心JVM是否會返回原始字串的拷貝,你只需要為 isCopy傳遞NULL作為引數。
由以上程式碼,我們可以清晰的看到String處理函式分為了Unicode和UTF-8兩種編碼型別,但在Android中使用的UTF-8相關的函式,兩種編碼提供的功能都基本一致,從函式名就可以看出對應函式的功能。下面通過一個使用C++合併String的例子來演示如何使用:
//Java中定義Native函式
public native static String strConcat(String str1,String str2);
//生成C++函式並用C++實現字串連線功能
#include "string.h"
JNIEXPORT jstring JNICALL Java_com_example_wastrel_test_Test_strConcat
(JNIEnv *env, jclass clazz, jstring str1, jstring str2){
//將jstring轉換成const char*指標,使用const修飾符表示其內容不可被修改
const char* c1=env->GetStringUTFChars(str1, NULL);
const char* c2=env->GetStringUTFChars(str2, NULL);
//計算新字串的長度
int size=strlen(c1)+strlen(c2);
//建立一個新的字串,這裡長度+1是為了使字串有結尾標記'\0'
char * n_char=new char[size+1];
//利用C標準庫提供的字串操作方法對字串進行連線,這裡需要include"string.h"標頭檔案
strcpy(n_char,c1);
strcat(n_char,c2);
//將生成的新字串轉換成UTF的jstring
jstring rs=env->NewStringUTF(n_char);
//刪除剛剛分配的記憶體 避免引起記憶體洩漏
delete [] n_char;
//通知JVM虛擬機器Native程式碼不在持有字串的引用,說明白點,就是告訴虛擬機器我不使用它了,你可以回收了。
//因為在JVM中如果物件被引用,那麼物件將不會被回收。
//這裡為什麼要傳遞jstring和生成的char*呢?是因為char*有可能是jstring的拷貝,如果是拷貝,那麼char*就應該被刪除。
env->ReleaseStringUTFChars(str1,c1);
env->ReleaseStringUTFChars(str2,c2);
return rs;
}
//然後我們在Java中呼叫該函式
print(Test.strConcat("里約奧運",",中國加油"));
注:本處以及往後程式碼中使用的print函式僅僅是把結果追加顯示在介面上的TextView上程式碼如下:
private void print(String str)
{
tv.append(str+"\n");
}
生成C函式過程請參照上一篇文章:http://blog.csdn.net/venusic/article/details/52121254/
執行結果:
基礎資料的陣列傳遞
陣列傳遞跟字串傳遞一樣,Native收到的都是引用的形式,因此JNIEnv也提供了一些列的方法來完成資料的轉換。因為不同資料型別之間的呼叫方式基本一致,此處使用int型陣列作為講解,int[]傳遞到Native後收到的是jintArray物件。
//獲得陣列的長度,該方法適用於所有jarray物件
jsize GetArrayLength(jarray array)
//在本地建立一個jint陣列,這個陣列只能通過SetIntArrayRegion賦值。
jintArray NewIntArray(jsize length);
//將jintArray轉換成jint指標
jint* GetIntArrayElements(jintArray array, jboolean* isCopy);
//取出陣列中的部分元素放在buf裡
void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf);
//給jintArray按區間賦值
void SetIntArrayRegion(jintArray array, jsize start, jsize len,const jint* buf);
//釋放jntArray,第一個參數列示傳過來的jintArray,第二參數列示獲取到的本地陣列指標
//第三個引數需要重點說明,該引數有三個取值:0、JNI_COMMIT、JNI_ABORT
//取值 零(0) 時,更新陣列並釋放所有元素;
//取值 JNI_COMMIT 時,更新但不釋放所有元素;
//取值 JNI_ABORT 時,不作更新但釋放所有元素;
//一般實際應用中取0較多
void ReleaseIntArrayElements(jintArray array, jint* elems,jint mode);
例子1:來自JNI的int陣列
//Java中定義Native函式,size表示返回陣列的大小
public native static int[] getIntArray(int size);
//生成Native函式並實現方法
#include "stdlib.h"
#include "time.h"
//定義隨機數產生巨集 表示產生0~x之間的隨機數
#define random(x) (rand()%x)
JNIEXPORT jintArray JNICALL Java_com_example_wastrel_test_Test_getIntArray
(JNIEnv *env, jclass clazz, jint size){
//用時間變數初始化隨機數產生器
srand((int)time(0));
jint* rs=new jint[size];
for (int i=0;i<size;i++)
{
//呼叫巨集產生0~100的隨機數
rs[i]=random(100);
}
//通過JNIEnv的NewIntArray方法new一個jintArray物件
jintArray array=env->NewIntArray(size);
//把產生的隨機數值賦值給jintArray
env->SetIntArrayRegion(array,0,size,rs);
return array;
}
//Java中呼叫函式
int []rs=Test.getIntArray(10);
print("來自於JNI的Int陣列");
print(IntArrayToString(rs));
/**將int[]轉換成逗號分隔便於顯示的輔助函式*/
private String IntArrayToString(int[] ints)
{
StringBuilder str=new StringBuilder();
str.append('[');
for (int i:ints)
{
str.append(i);
str.append(',');
}
str.deleteCharAt(str.length()-1);
str.append(']');
return str.toString();
}
執行結果:
例子2:使用JNI對例1返回的陣列進行排序
//宣告Java Native函式,引數為int[]
public native static void sortIntArray(int []ints);
//實現C++函式
JNIEXPORT void JNICALL Java_com_example_wastrel_test_Test_sortIntArray
(JNIEnv *env, jclass clazz, jintArray array){
//獲得傳遞過來的陣列長度
jsize size=env->GetArrayLength(array);
//將陣列轉換成Java指標
jint* jints=env->GetIntArrayElements(array,NULL);
//簡單的氣泡排序
for (int i = 0; i <size-1 ; ++i) {
for (int j = 0; j <size-1-i ; ++j) {
if(jints[j]<jints[j+1])
{
int t=jints[j];
jints[j]=jints[j+1];
jints[j+1]=t;
}
}
}
//將排序結果更新到Java陣列中,第三個引數等於0表明更新到原陣列並釋放所有元素
env->ReleaseIntArrayElements(array,jints,0);
return;
}
//在Java中呼叫
print("通過JNI對int陣列排序:");
Test.sortIntArray(rs);
print(IntArrayToString(rs));
這裡可以看到,我們並沒有返回陣列,而是通過ReleaseIntArrayElements函式將結果更新到Java陣列中。隨之我們在Java中的陣列值已經變更。
執行結果:
其他基礎型別陣列的傳遞
其他基礎型別中的陣列傳遞與int[]幾乎一致,函式名更換成各自的型別即可。這裡不在過多敘述,我想有了上面的例子,很容易明白別的基礎資料型別傳遞使用。
示例程式下載地址:http://download.csdn.net/detail/venusic/9604128
相關文章
- Android Studio NDK 入門教程(3)--Java與C++之間的型別簽名AndroidJavaC++型別
- react元件與元件之間的資料傳遞React元件
- Mac之Android Studio開發NDK入門MacAndroid
- iOS應用之間的跳轉與資料傳遞iOS
- 更為簡單的JsonString資料與Map,List之間的轉換JSON
- 字串與資料流之間的轉換字串
- Java中Array與ArrayList之間的轉換Java
- Java 第9 章 : 資料表與簡單Java類對映轉換Java
- Android Studio NDK 入門教程(1)--來自C 語言的StringAndroid
- struts2的action與jsp之間傳遞引數JS
- C++中char*與wchar_t*之間的轉換C++
- Java 資料型別之間的轉換Java資料型別
- vue元件之間的資料傳遞Vue元件
- c++指標傳遞與引用傳遞C++指標
- 頁面之間傳遞資料
- Selenium2(webdriver)入門之TestNG的安裝與簡單使用Web
- NSData與UIImage之間的轉換UI
- 最簡單的Flutter路由教程——跳轉、動畫與傳參Flutter路由動畫
- Android Bitmap 與 Drawable之間的區別和轉換Android
- 【Java】基本資料、包裝類間轉換與處理Java
- [Java] 基本資料、包裝類間轉換與處理Java
- Android NDK開發中java資料型別與C/C++資料型別的對應關係AndroidJava資料型別C++
- Android NDK入門:C++ 基礎知識AndroidC++
- C/C++ 時間轉換與表示C++
- Swift3.0語言教程字串與URL的資料轉換與自由轉換Swift字串
- Android Studio3.0 NDK配置與開發Android
- JDBC入門與簡單使用JDBC
- [2]SpinalHDL教程——Scala簡單入門
- ANSI與UTF8之間的轉換!std::string與UTF8之間的轉換
- JAVA進階:VO(DTO)與PO(DAO)之間的轉換Java
- android中String與InputStream之間的相互轉換方式Android
- Git與GitHub入門簡明教程Github
- SpringBoot2.x入門教程:引入jdbc模組與JdbcTemplate簡單使用Spring BootJDBC
- Java NIO系列教程(五) 通道之間的資料傳輸Java
- 淺入深出Vue:子元件與資料傳遞Vue元件
- Java與Json資料格式轉換JavaJSON
- bundle實現Activity之間的資料傳遞
- 不同順序InBoundHandler之間的資料傳遞