传递和返回其他原始数据类型(java和C/C++)
传递和返回其他原始数据类型(java和C/C++)
从原生代码里面引用java对象
行动时间——保存一个关于Store对象的引用
1. 在com.packtpub包里面,创建一个新的类Color包含了对color的完整描述。这个整型从语法上来讲是一个String。
下面是android.graphics.Color的定义
public class Color {
private int mColor;
public Color(String pColor) {
super();
mColor = android.graphics.Color.parseColor(pColor);
}
@Override
public String toString() {
return String.format(“#%06X”, mColor);
}
}
2.重写StoreType使他包含新的Color数据类型:
public enum StoreType {
Integer, String, Color
}
3.打开Store.java添加两个新的方法来获取和保存Color对象:
public class Store {
static {
System.loadLibrary(“store”);
}
...
public native Color getColor(String pKey);
public native void setColor(String pKey, Color pColor);
}
4.打开StoreActivity.java更新onGetValue()和onSetValue()来显示和描述Color实例。需要注意的是如果color代码不正确color可能会产生IllegalArgumentException
public class StoreActivity extends Activity {
...
private void onGetValue() {
String lKey = mUIKeyEdit.getText().toString();
StoreType lType = (StoreType) mUITypeSpinner
.getSelectedItem();
switch (lType) {
...
case Color:
mUIValueEdit.setText(mStore.getColor(lKey).toString());
break;
}
}
private void onSetValue() {
String lKey = mUIKeyEdit.getText().toString();
String lValue = mUIValueEdit.getText().toString();
StoreType lType = (StoreType) mUITypeSpinner
.getSelectedItem();
try {
switch (lType) {
...
case Color:
mStore.setColor(lKey, new Color(lValue));
break;
}
}
catch (NumberFormatException eNumberFormatException) {
displayError(“Incorrect value.”);
} catch (IllegalArgumentException eIllegalArgumentException)
{
displayError(“Incorrect value.”);
}
}
...
}
java代码已经完成,我们将在原生代码里面写方法来获取和保存color对象
5.在jni/Store.h里面,给StoreType添加一个新的color类型,并且在StoreValue里面添加一个新的成员变量
但是我们要添加什么类型呢?由于Color仅仅是在java层知道的一个对象。在JNI里面,所有java对象有相同的类型:
jobject 一个对象的引用:
...
typedef enum {
StoreType_Integer, StoreType_String, StoreType_Color
} StoreType;
typedef union {
int32_t mInteger;
char* mString;
jobject mColor;
} StoreValue;
...
6.使用javah重新生成JNI头文件 jni/com_packtpub_Store.h
7.现在已经生成了两个新的函数原型 getColor() 和 setColor() 。我们接着来实现他们 ,第一个就简单的返回java方面的Color对象,真正细微的地方是在第2个函数里面。事实上,表面看简单的保存jobject就足够了,但这种认为是错的。参数中传递的对象或者JNI方法里面生成的都是 local references 本地引用。java本地引用不能被原生代码或者外部引用。
为了能够在原生代码里面使用java对象的引用,他应该被转换成全局引用,告诉Dalvik Vm不能把他回收掉。为了实现JNI API提供了NewGlobalRef()以及对应的DeleteGlobalRef()。全局引用在分配失败的时候就会删除:
#include “com_packtpub_Store.h”
#include “Store.h”
...
JNIEXPORT jobject JNICALL Java_com_packtpub_Store_getColor
(JNIEnv* pEnv, jobject pThis, jstring pKey) {
StoreEntry* lEntry = findEntry(pEnv, &gStore, pKey, NULL);
if (isEntryValid(pEnv, lEntry, StoreType_Color)) {
return lEntry->mValue.mColor;
} else {
return NULL;
}
}
JNIEXPORT void JNICALL Java_com_packtpub_Store_setColor
(JNIEnv* pEnv, jobject pThis, jstring pKey, jobject pColor) {
jobject lColor = (*pEnv)->NewGlobalRef(pEnv, pColor);
if (lColor == NULL) {
return;
}
StoreEntry* lEntry = allocateEntry(pEnv, &gStore, pKey);
if (lEntry != NULL) {
lEntry->mType = StoreType_Color;
lEntry->mValue.mColor = lColor;
} else {
(*pEnv)->DeleteGlobalRef(pEnv, lColor);
}
}
...
8.使用NewGlobalRef()之后就必须对应的使用DeleteGlobalRef()。在我们的例子中当entry被替换的时候就必须删除全局引用。
在Store.c里面修改releaseEntryValue();
...
void releaseEntryValue(JNIEnv* pEnv, StoreEntry* pEntry) {
switch (pEntry->mType) {
...
case StoreType_Color:
(*pEnv)->DeleteGlobalRef(pEnv, pEntry->mValue.mColor);
break;
}
}