Android NDK : 基本类型,字符串与数组
Java与native的交互 :基本类型
示例1:获取native返回的整型数据
这个例子比较简单,只是返回一个整型数据,我们可以在android的java代码中接收这个值。
#include"com_cumt_ndktest1_GetInt.h"
#include<stdlib.h>
jint getint(){
return 100;
}
JNIEXPORT jint JNICALL Java_com_cumt_ndktest1_GetInt_getIntNumbe
(JNIEnv *env, jobject obj)
{
return getint();
}
示例2:在native函数中修改java变量
这个例子稍微复杂了一些,是关于native修改java的一个字符串的,对于在native中修改java中的整型变量也是类似的 。首先要知道“域”(field)的概念。在java编程中支持两种域,
一种是对于每一个class的实例,都有其对应的一个实例域(instance field)。一个类的所有实例共享同一个静态域(static field)。“域”的作用是什么呢?从下面的代码中我们可以
#include "com_cumt_ndktest10_InstanceFieldAccess.h"
#include <stdio.h>
/**
* 在native函数中访问java的成员变量,并在native函数中
* 修改成员变量的值。
* @author wangcan
*
*/
JNIEXPORT void JNICALL Java_com_cumt_ndktest10_InstanceFieldAccess_accessField(
JNIEnv *env, jobject obj) {
jfieldID fid; /* store the field ID */
jstring jstr;
const char *str;
/* Get a reference to obj’s class */
jclass cls = (*env)->GetObjectClass(env, obj);
/* Look for the instance field s in cls */
fid = (*env)->GetFieldID(env, cls, "s", "Ljava/lang/String;");
if (fid == NULL) {
return; /* failed to find the field */
}
/* Read the instance field s */
jstr = (*env)->GetObjectField(env, obj, fid);
str = (*env)->GetStringUTFChars(env, jstr, NULL);
if (str == NULL) {
return; /* out of memory */
}
printf(" c.s = \"%s\"\n", str);
(*env)->ReleaseStringUTFChars(env, jstr, str);
/* Create a new string and overwrite the instance field */
jstr = (*env)->NewStringUTF(env, "123");
if (jstr == NULL) {
return; /* out of memory */
}
(*env)->SetObjectField(env, obj, fid, jstr);
}
注意这句代码:
fid = (*env)->GetFieldID(env,cls, "s", "Ljava/lang/String;");
第二个参数是jclass,即对应的java类(声明native方法的java类)。第三个参数是变量的名称,比如我们java中定义了一个字符串s ,这里就写”s” 。第四个参数是域的描述符(field descriptor)。对于int型,对应 I ,对于float型对应 F ,double型对应 D,boolean型对应Z 。
示例三:在native中修改java静态变量的值
#include "com_cumt_ndktest11_StaticFieldAccess.h"
#include<stdio.h>
JNIEXPORT void JNICALL Java_com_cumt_ndktest11_StaticFieldAccess_accessField
(JNIEnv *env, jobject obj)
{
jfieldID fid; /* store the field ID */
jint si;
/* Get a reference to obj’s class */
jclass cls = (*env)->GetObjectClass(env, obj);
printf("In C:\n");
/* Look for the static field si in cls */
fid = (*env)->GetStaticFieldID(env, cls, "si", "I");
if (fid == NULL) {
return; /* field not found */
}
/* Access the static field si */
si = (*env)->GetStaticIntField(env, cls, fid);
printf(" StaticFieldAccess.si = %d\n", si);
(*env)->SetStaticIntField(env, cls, fid, 200);
}
对于静态变量的操作其实与前面的成员变量的类似,只是调用的方法发生了改变。
Java与native的交互:字符串
示例1:生成新的字符串
//com_cumt_ndktest2_HelloAndroidNDK.c
#include "com_cumt_ndktest2_HelloAndroidNDK.h"
JNIEXPORT jstring JNICALL Java_com_cumt_ndktest2_HelloAndroidNDK_sayHelloToNDK
(JNIEnv *env, jobject thiz)
{
return (*env)->NewStringUTF(env,"Hello Android NDK");
}
使用NewStringUTF可以生成一个UTF-8 类型的C字符串,若果不想使用UTF-8还有一个NewString方法来生成一个C字符串。
示例2:获取子串与获取字符串长度
#include "com_cumt_ndktest7_Prompt.h"
#include <stdio.h>
JNIEXPORT jstring JNICALL Java_com_cumt_ndktest7_Prompt_getLine
(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const jbyte *str;
str = (*env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL) {
return NULL; /* OutOfMemoryError already thrown */
}
//printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
/* We assume here that the user does not type more than
* 127 characters */
//scanf("%s", buf);
char outbuf[128];
//获取字符串长度
int len = (*env)->GetStringLength(env, prompt);
len -= 3;
//获取子串
(*env)->GetStringUTFRegion(env, prompt, 0, len, outbuf);
return (*env)->NewStringUTF(env, outbuf);
}
这里来说明一下代码中用到的方法:
GetStringUTFChars : 获取指向一个UTF-8编码的字符串的指针,返回字符串的拷贝
ReleaseStringUTFChars:释放指向一个UTF-8编码的字符串的指针。
GetStringLength:返回字符串的Unicode字符集的字符数目。
GetStringUTFRegion:拷贝一个字符串的内容,根据起始和终止位置,要注意是UTF-8格式。
Java与native的交互:数组
示例1:一维数组
#include "com_cumt_ndktest8_IntArray.h"
JNIEXPORT jint JNICALL Java_com_cumt_ndktest8_IntArray_sumArray
(JNIEnv *env, jobject object, jintArray arr)
{
/*
* 第一种使用方法:使用GetIntArrayElements实现
* jint *carr;
jint i, sum = 0;
carr = (*env)->GetIntArrayElements(env, arr, NULL);
if (carr == NULL) {
return 0;
}
for (i=0; i<10; i++) {
sum += carr[i];
}
(*env)->ReleaseIntArrayElements(env, arr, carr, 0);
return sum;
*/
//使用GetIntArrayRegion函数实现
jint buf[10];
jint i, sum = 0;
(*env)->GetIntArrayRegion(env, arr, 0, 10, buf);
for (i = 0; i < 10; i++) {
sum += buf[i];
}
return sum;
}
对应已知字符串长度的字符串我们可以使用上面方法中没注释的内容,因为这样我们可以自己设定缓冲区的大小,对
于不清楚字符串长度的时候选择第一种方法。大多数情况下采取这种策略就OK了。
示例2:二维数组
#include "com_cumt_ndktest9_ObjectArrayTest.h"
#include <stdio.h>
JNIEXPORT jobjectArray JNICALL Java_com_cumt_ndktest9_ObjectArrayTest_initInt2DArray(
JNIEnv *env, jclass cls, jint size) {
/**
* 根据jint size来生成一个 size * size 的二维数组
* 返回值 : 生成的二维数组
*/
jobjectArray result;
int i;
jclass intArrCls = (*env)->FindClass(env, "[I");
if (intArrCls == NULL) {
return NULL; /* exception thrown */
}
result = (*env)->NewObjectArray(env, size, intArrCls, NULL);
if (result == NULL) {
return NULL; /* out of memory error thrown */
}
for (i = 0; i < size; i++) {
jint tmp[256]; /* make sure it is large enough! */
int j;
jintArray iarr = (*env)->NewIntArray(env, size);
if (iarr == NULL) {
return NULL; /* out of memory error thrown */
}
for (j = 0; j < size; j++) {
tmp[j] = i + j;
}
(*env)->SetIntArrayRegion(env, iarr, 0, size, tmp);
(*env)->SetObjectArrayElement(env, result, i, iarr);
(*env)->DeleteLocalRef(env, iarr);
}
return result;
}