Android Ndk(Beginner ‘s guide)(3.5)

Jni里面Java数组的处理

  这里还有一种数据类型我们没有讨论过:数组。数组在JNI里面有一个特殊的作用。他有他们特有的格式以及特有的API,然而Java数组仍然是他的基础。我们来修改Store类使得他能使用数组来同时输入一些列的值。这系列值将会以java数组类型传递到原生代码中,在原生代码中以C数组类型存储。

行动时间——在Store里面保存一个对象的引用

我们仍然先从java代码方面着手:
1.我们下载一个帮助库——Google Guava来过帮助我们处理数组,点击下载。Guava提供了许多游泳的函数来处理原型数据以及数组。复制guava jar到libs目录下。
2.打开工程的Properties进入java Build Path栏,在library选项里面,点击Add JARs来引用GUava。
3.编辑前面生成的StoreType,向里面添加两个新的值 IntegerArray和ColorArray:
public enum StoreType {
Integer, String, Color,
IntegerArray, ColorArray
}
4.打开Sotre.java向里面添加新函数来获取和保存ini和Color数组:
public class Store {
static {
System.loadLibrary(“store”);
}
...
public native int[] getIntegerArray(String pKey)
throws NotExistingKeyException;
public native void setIntegerArray(String pKey, 
int[] pIntArray);
public native Color[] getColorArray(String pKey)
throws NotExistingKeyException;
public native void setColorArray(String pKey,
Color[] pColorArray);
}
5.最后在StoreActivity.java里面实现原生代码与GUI的连接。首先,onGetValue()从store获取数组,其中的值用分号隔开。最后显示他们:
public class StoreActivity extends Activity {
...
private void onGetValue() {
String lKey = mUIKeyEdit.getText().toString();
StoreType lType = (StoreType) mUITypeSpinner
.getSelectedItem();
try {
switch (lType) {
...
case IntegerArray:
mUIValueEdit.setText(Ints.join(“;”,
mStore.getIntegerArray(lKey)));
break;
case ColorArray:
mUIValueEdit.setText(Joiner.on(“;”).join(
mStore.getColorArray(lKey)));
break;
}
}
catch (NotExistingKeyException eNotExistingKeyException) {
displayError(“Key does not exist in store”);
} catch (InvalidTypeException eInvalidTypeException) {
displayError(“Incorrect type.”);
}
}
...
6.在StoreActivity.java里面修改onSetvalue()函数以便在发送数据到Store去之前转换用户输入的值成数组类型。使用Guava的转换功能来完成这个任务:一个Function对象转换字符串类型到目标类型将会使用帮助函数stringToList()。运行之前,将会根据用户的分号来分隔数据。
...
private void onSetValue() {
String lKey = mUIKeyEdit.getText().toString();
String lValue = mUIValueEdit.getText().toString();
StoreType lType = (StoreType) mUITypeSpinner
.getSelectedItem();
try {
switch (lType) {
...
case IntegerArray:
mStore.setIntegerArray(lKey,
Ints.toArray(stringToList(
new Function<String, Integer>() {
public Integer apply(String pSubValue) {
return Integer.parseInt(pSubValue);
}
}, lValue)));
break;
case ColorArray:
List<Color> lIdList = stringToList(
new Function<String, Color>() {
public Color apply(String pSubValue) {
return new Color(pSubValue);
}
}, lValue);
Color[] lIdArray = lIdList.toArray(
new Color[lIdList.size()]);
mStore.setColorArray(lKey, lIdArray);
break;
}
}
catch (NumberFormatException eNumberFormatException) {
displayError(“Incorrect value.”);
} catch (IllegalArgumentException eIllegalArgumentException) 
{
displayError(“Incorrect value.”);
} catch (StoreFullException eStoreFullException) {
displayError(“Store is full.”);
}
}
private <TType> List<TType> stringToList(
Function<String, TType> pConversion,
String pValue) {
String[] lSplitArray = pValue.split(“;”);
List<String> lSplitList = Arrays.asList(lSplitArray);
return Lists.transform(lSplitList, pConversion);
}
}
7.打开jni/Store.h,向StoreType里面添加新的数组类型。同时在StoreValue里面声明两个新的类型mIntegerArray和mColorArray.Store数组代表了C数组。 我们同样不要忘记数组长度。在StoreEntry里面添加mLength
#ifndef _STORE_H_
#define _STORE_H_
#include “jni.h”
#include <stdint.h>
#define STORE_MAX_CAPACITY 16
typedef enum {
StoreType_Integer, StoreType_String, StoreType_Color,
StoreType_IntegerArray, StoreType_ColorArray
} StoreType;
typedef union {
int32_t mInteger;
char* mString;
jobject mColor;
int32_t* mIntegerArray;
jobject* mColorArray;
} StoreValue;
typedef struct {
char* mKey;
StoreType mType;
StoreValue mValue;
int32_t mLength;
} StoreEntry;
...
8.打开jni/Store.c在里面添加新的方法releaseEntryValue()。数组分配的空间在先关的输入值取消的时候需要被回收。因为color是java对象,删除全局引用否则就不能回收废弃空间。
...
void releaseEntryValue(JNIEnv* pEnv, StoreEntry* pEntry) {
int32_t i;
switch (pEntry->mType) {
...
case StoreType_IntegerArray:
free(pEntry->mValue.mIntegerArray);
break;
case StoreType_ColorArray:
for (i = 0; i < pEntry->mLength; ++i) {
(*pEnv)->DeleteGlobalRef(pEnv,
pEntry->mValue.mColorArray[i]);
}
free(pEntry->mValue.mColorArray);
break;
}
}
...
9.重新生成JNI 头文件jni/com_packtpub_Store.h
10. 实现com_packtpub_Store.c里面的所有方法,以getIntegerArray()开始。JNI的整型数组是由jintArray代替。如果int相当于jint的话,int* 却绝不等于jintArray,前面的是指向内存缓冲区的指针,而后面的却是一个对象的引用。
因此为了返回一个jintArray,需要用到JNI的API函数NewIntArray().然后使用SetIntArrayRegin()把原生里面的int流连接到jintArray里面。
SetIntArrayRegion()实施越界检查来避免数据溢出,并能返回ArrayIndexOurofBoundsException()。但是这却没有必要检查,因为后面没有操作了。
#include “com_packtpub_Store.h”
#include “Store.h”
...
JNIEXPORT jintArray JNICALL Java_com_packtpub_Store_
getIntegerArray
(JNIEnv* pEnv, jobject pThis, jstring pKey) {
StoreEntry* lEntry = findEntry(pEnv, &gStore, pKey, NULL);
if (isEntryValid(pEnv, lEntry, StoreType_IntegerArray)) {
jintArray lJavaArray = (*pEnv)->NewIntArray(pEnv,
lEntry->mLength);
if (lJavaArray == NULL) {
return;
}
(*pEnv)->SetIntArrayRegion(pEnv, lJavaArray, 0,
lEntry->mLength, lEntry->mValue.mIntegerArray);
return lJavaArray;
} else {
return NULL;
}
}
...
11.为了在原生代码里面保存java数组,相反的操作也存在GetIntArratRegion()。给目标划分适合的存储空间的唯一方法就是使用GetArrayLength()来测试数组的大小。GetIntArrayRegion()同样有越界检查以及能跑出异常。因此使用ExceptionCheck()检测到异常的时候就必须立刻停下来。尽管GetIntArrayRegion()并不是唯一能抛出异常的函数,但是他有与SetIntArrayRegion()同样地性质 可以返回void。因为不存在返回值的检查,所以要异常检查:
...
JNIEXPORT void JNICALL Java_com_packtpub_Store_setIntegerArray
(JNIEnv* pEnv, jobject pThis, jstring pKey, jintArray 
pIntegerArray) {
jsize lLength = (*pEnv)->GetArrayLength(pEnv, pIntegerArray);
int32_t* lArray = (int32_t*) malloc(lLength * 
sizeof(int32_t));
(*pEnv)->GetIntArrayRegion(pEnv, pIntegerArray, 0, lLength, 
lArray);
if ((*pEnv)->ExceptionCheck(pEnv)) {
free(lArray);
return;
}
StoreEntry* lEntry = allocateEntry(pEnv, &gStore, pKey);
if (lEntry != NULL) {
lEntry->mType = StoreType_IntegerArray;
lEntry->mLength = lLength;
lEntry->mValue.mIntegerArray = lArray;
} else {
free(lArray);
return;
}
}
...
12.对象数组与原始数据类型数组是不同的。他们是由Class类型初始化的。对象数组是由jobjectArray类型代替。相反在原始数组方面,所有元素不可能同时工作。相反,对象可以通过SetObjectArrayElement()一个一个的设置。这里数组里面保存的是存放在原生代码里面的Color的对象,他保持了对Color对象的引用。因此没有必须再对他进行引用。
...
JNIEXPORT jobjectArray JNICALL Java_com_packtpub_Store_
getColorArray
(JNIEnv* pEnv, jobject pThis, jstring 
pKey) {
StoreEntry* lEntry = findEntry(pEnv, &gStore, pKey, NULL);
if (isEntryValid(pEnv, lEntry, StoreType_ColorArray)) {
jclass lColorClass = (*pEnv)->FindClass(pEnv,
“com/packtpub/Color”);
if (lColorClass == NULL) {
return NULL;
}
jobjectArray lJavaArray = (*pEnv)->NewObjectArray(
pEnv, lEntry->mLength, lColorClass, NULL);
(*pEnv)->DeleteLocalRef(pEnv, lColorClass);
if (lJavaArray == NULL) {
return NULL;
}
int32_t i;
for (i = 0; i < lEntry->mLength; ++i) {
(*pEnv)->SetObjectArrayElement(pEnv, lJavaArray, i,
lEntry->mValue.mColorArray[i]);
if ((*pEnv)->ExceptionCheck(pEnv)) {
return NULL;
}
}
return lJavaArray;
} else {
return NULL;
} }
...
13.在setColorArray()函数里,数组元素同样通过GetObjcetArrayElement*(一个一个的取出来。返回的引用是本地引用,应该转换成全局引用以保证其在内存缓冲里面的安全。如果发生了错误,全局引用应该小心的回收掉。
...
JNIEXPORT void JNICALL Java_com_packtpub_Store_setColorArray 
(JNIEnv*
pEnv, jobject pThis, jstring pKey, jobjectArray 
pColorArray) {
jsize lLength = (*pEnv)->GetArrayLength(pEnv, pColorArray);
jobject* lArray = (jobject*) malloc(lLength * 
sizeof(jobject));
int32_t i, j;
for (i = 0; i < lLength; ++i) {
jobject lLocalColor = (*pEnv)->GetObjectArrayElement(pEnv,
pColorArray, i);
if (lLocalColor == NULL) {
for (j = 0; j < i; ++j) {
(*pEnv)->DeleteGlobalRef(pEnv, lArray[j]);
}
free(lArray);
return;
}
lArray[i] = (*pEnv)->NewGlobalRef(pEnv, lLocalColor);
if (lArray[i] == NULL) {
for (j = 0; j < i; ++j) {
(*pEnv)->DeleteGlobalRef(pEnv, lArray[j]);
}
free(lArray);
return;
}
(*pEnv)->DeleteLocalRef(pEnv, lLocalColor);
}
StoreEntry* lEntry = allocateEntry(pEnv, &gStore, pKey);
if (lEntry != NULL) {
lEntry->mType = StoreType_ColorArray;
lEntry->mLength = lLength;
lEntry->mValue.mColorArray = lArray;
} else {
for (j = 0; j < i; ++j) {
(*pEnv)->DeleteGlobalRef(pEnv, lArray[j]);
}
free(lArray);
return;
}
}

刚刚实现了什么?

我们实现了Java数组与C数组的互换。java的数组是对象,不能直接在C原生代码里面处理必须通过特定API处理。
原生可用的数组类型有:jbooleanArray, jbyteArray, jcharArray, jdoubleArray, jfloatArray, jlongArray, 和jshortArray.这些数组是通过“set”来使用的。也就是说一次只能有几个可以使用。下面是几种访问数据内容的方法:

对象数组是一个特殊的情况,因为原始数据数组中每个元素是一个引用可以被垃圾回收机制回收。因此当掺入一个数组的时候一个新的引用就自动生成了。那样即使调用代码移除了对他的引用,数组仍然引用了他们。对象数组就是通过GetObjectArrayElement和SetObjectArrayElement来操作。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值