Android Ndk(Beginner ‘s guide)(3.2)

传递和返回其他原始数据类型(java和C/C++)

    前面的store类仅仅处理了整型和字符串类型的数据。基于这个原型,试着继承store方法来实现其他的原始类型:boolean,byte
char,double,float,long和short.

从原生代码里面引用java对象

从上一节里面我们知道在JNI里面string是以jstring来代替的。事实上可以通过JNI来转换Java对象。但是由于原生代码不能明白或者直接解析Java,所以所有java对象都有了一个代替类型jobject
在这部分,我们将实现怎么在本地生成一个对象,然后把它传递给java。在下一个项目,我们会使用colors,以及其他可能使用的类型。

行动时间——保存一个关于Store对象的引用

首先我们在Java里面添加一个Color数据类型:

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;
}
}

刚刚完成了什么?

运行程序,保存一个color的值例如#FF0000或者red,然后在store里面取回。我们已经实现了在原生代码里面保存java对象

所有来自Java的对象都用jobject代替。即使是jstring,一个用jobject包装的原型,也是这样使用的。因为原生代码安全域限制在函数体内。Jni保证了对象的引用是在本地。这就意味着jobject只能在传递目标的函数内部才能安全使用。事实上,Dalvik VM 能够在函数运行之前或者以后请求本地代码的使用,以及控制java 对象的引用。但是jobject仅仅只是一个指向实际对象的包装,并没有什么其他特性以及垃圾回收机制。一旦原生方法返回之后,Dalvik VM就不知道是否原生代码仍然保持对对象的引用,或者什么时候去回收他们。
为了在范围外使用全局引用,引用就必须使用NewGolbalRef() 以及用DeleteGlobalRef()解引用。如果不使用后者,Dalvik VM就永远不会回收他,认为他一直被引用着。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值