分享

NDK 之 JNI数据传输

 勤奋不止 2013-05-30

1 基本数据类型的传输

上层定义一个native的方法,需要一个int 参数 ,返回一个int值

JNI 对应上层的方法 , 打印出上层 传输下来的 int数据,并返回 int数据

上层 收到 native 方法 返回的 值,在UI中显示出来

public native int getNumber(int num);

jint
Java_XX_XX_XXActivity_getNumber(JNIEnv* env,jobject thiz,jint num)
{
    if(jniEnv == NULL) {
        jniEnv = env;
    }
    __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "Java -- > C JNI : num = %d",num);
    return num*2;
}

 

2 数组的传输

上层定义一个native的方法,需要一个int数组,返回一个int数组

JNI 对应上层的方法,取出上层传递数组中的数据处理和打印出来,并存入新数组中,最后把该数组返回给 Java层

上层 收到 native返回的 数组,加工成字符串,在UI中显示出来

public native int[] getArrayNumber(int[] nums);

 

JNIEnv* jniEnv;
 
jintArray
Java_XX_XX_XXActivity_getArrayNumber(JNIEnv* env,jobject thiz,jintArray nums)
{
    if(jniEnv == NULL) {
        jniEnv = env;
    }
    if(nums == NULL){
        return NULL;
    }
    jsize len = (*jniEnv)->GetArrayLength(jniEnv, nums);
    if(len <= 0) {
        return NULL;
    }
    jintArray array = (*jniEnv)->NewIntArray(jniEnv, len);
    if(array == NULL) {
        return NULL;
    }
    // 把 Java 传递下来的数组 用 jint* 存起来
    jint *body = (*env)->GetIntArrayElements(env, nums, 0);
    jint i = 0;
    jint num[len];
    for (; i < len; i++) {
        num[i] = body[i] * 2;
    }
    if(num == NULL){
        return NULL;
    }
//(*env)->GetIntArrayRegion(env,array,start,len,buffer)
// 从start开始复制长度为len 的数据到buffer中
    //给需要返回的数组赋值
    (*jniEnv)->SetIntArrayRegion(jniEnv,array, 0, len, num);
    return array;
}
 

3   引用数据类型

 

String 字符串传输

上层定义一个native的方法,需要一个String 参数,返回一个String

JNI对应上层的方法,打印出上层传输下来的String数据,并返回处理String数据

上层 收到 native 方法 返回的 值,在UI中显示出来

public native String transferString(String mStrMSG);

jstring
Java_XX_XX_XXActivity_transferString( JNIEnv* env,jobject thiz,jstring msg )
{
    if(jniEnv == NULL) {
        jniEnv = env;
    }
     char data[128];
    memset(data, 0, sizeof(data));
    char *c_msg = NULL;
    c_msg = (char *)(*jniEnv)->GetStringUTFChars(jniEnv, msg, 0);
     __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "C JNI  ---- > %s",c_msg);
     return (*jniEnv)->NewStringUTF(jniEnv, "This is send by C JNI");
}

 

 

自定义对象的传输

自定义一个对象Person

上层定义一个native方法,参数Person,返回值Person

JNI接收对象,打印出相关信息数据

JNI 修改Person 对象数据,并返回到上层

上层接收到数据后 在UI显示出来

public native Object transferPerson(Person mPerson);     
 
public class Person {
    private String name;
    private int age;
    public Person() {
        name = "";
        age = 0;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}
 
extern JNIEnv* jniEnv;
jclass Person;
jobject mPerson;
jmethodID getName;
jmethodID setName;
jmethodID toString;
int InitPerson();
void ToString();
void GetName();
void SetName();
 
jobject
Java_XX_XX_XXActivity_transferPerson( JNIEnv* env,jobject thiz,jobject person )
{
    if(jniEnv == NULL) {
        jniEnv = env;
    }
    if (Person == NULL || getName == NULL || setName == NULL || toString == NULL) {
        if (1 != InitPerson()) {
            return NULL;
        }
    }
    mPerson = person;
    if(mPerson == NULL) {
        return NULL;
    }
    GetName();
    GetAge();
    ToString();
    __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "Begin Modify mPerson  .... " );
    SetName();
    SetAge();
    ToString();
    return mPerson;
}
 
int InitPerson() {
    if(jniEnv == NULL) {
        return 0;
    }
    if(Person == NULL) {
        Person = (*jniEnv)->FindClass(jniEnv,"com/XX/Person");
        if(Person == NULL){
            return -1;
        }
    }
    if (getName == NULL) {
        getName = (*jniEnv)->GetMethodID(jniEnv, Person, "getName","()Ljava/lang/String;");
        if (getName == NULL) {
            (*jniEnv)->DeleteLocalRef(jniEnv, Person);
            return -2;
        }
    }
    if (setName == NULL) {
        setName = (*jniEnv)->GetMethodID(jniEnv, Person, "setName","(Ljava/lang/String;)V");
        if (setName == NULL) {
            (*jniEnv)->DeleteLocalRef(jniEnv, Person);
            (*jniEnv)->DeleteLocalRef(jniEnv, getName);
            return -2;
        }
    }
    if (getAge == NULL) {
        getAge = (*jniEnv)->GetMethodID(jniEnv, Person, "getAge","()I");
        if (getAge == NULL) {
            (*jniEnv)->DeleteLocalRef(jniEnv, Person);
            (*jniEnv)->DeleteLocalRef(jniEnv, getName);
            (*jniEnv)->DeleteLocalRef(jniEnv, setName);
            return -2;
        }
    }
    if (setAge == NULL) {
        setAge = (*jniEnv)->GetMethodID(jniEnv, Person, "setAge","(I)V");
        if (setAge == NULL) {
            (*jniEnv)->DeleteLocalRef(jniEnv, Person);
            (*jniEnv)->DeleteLocalRef(jniEnv, getName);
            (*jniEnv)->DeleteLocalRef(jniEnv, setName);
            (*jniEnv)->DeleteLocalRef(jniEnv, getAge);
            return -2;
        }
    }
    if (toString == NULL) {
        toString = (*jniEnv)->GetMethodID(jniEnv, Person, "toString","()Ljava/lang/String;");
        if (toString == NULL) {
            (*jniEnv)->DeleteLocalRef(jniEnv, Person);
            (*jniEnv)->DeleteLocalRef(jniEnv, getName);
            (*jniEnv)->DeleteLocalRef(jniEnv, setName);
            (*jniEnv)->DeleteLocalRef(jniEnv, getAge);
            (*jniEnv)->DeleteLocalRef(jniEnv, setAge);
            return -2;
        }
    }
    return 1;
}
/**
* GetName  对应Person的getName方法
*/
void GetName() {
    if(Person == NULL || getName == NULL) {
        if(1 != InitPerson()){
            return;
        }
    }
    //调用方法
    jstring jstr = (*jniEnv)->CallObjectMethod(jniEnv, mPerson, getName);
    char* cstr = (char*) (*jniEnv)->GetStringUTFChars(jniEnv,jstr, 0);
    __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "getName  ---- >  %s",cstr );
    //释放资源
    (*jniEnv)->ReleaseStringUTFChars(jniEnv, jstr, cstr);
    (*jniEnv)->DeleteLocalRef(jniEnv, jstr);
}
/**
* GetAge 对应Person的getName方法
*/
void GetAge() {
    if(Person == NULL || getName == NULL) {
        if(1 != InitPerson()){
            return;
        }
    }
    //调用方法
    jint age = (*jniEnv)->CallIntMethod(jniEnv, mPerson, getAge);
    __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "getAge  ---- >  %d",age );
}
/**
* SetName 对应Person的setName方法
*/
void SetName() {
    if(Person == NULL || setName == NULL) {
        if(1 != InitPerson()){
            return;
        }
    }
    jstring jstr = (*jniEnv)->NewStringUTF(jniEnv, "Modify Name");
    //调用方法
    (*jniEnv)->CallVoidMethod(jniEnv, mPerson, setName,jstr);
    (*jniEnv)->DeleteLocalRef(jniEnv, jstr);
}
int age = 20;
/**
* SetAge 对应Person的setAge方法
*/
void SetAge() {
    if(Person == NULL || setAge == NULL) {
        if(1 != InitPerson()){
            return;
        }
    }
    //调用方法
    (*jniEnv)->CallVoidMethod(jniEnv, mPerson, setAge,age++);
}
/**
* ToString 对应 Person 的 toString 方法 , 打印出相关信息
*/
void ToString() {
    if(Person == NULL || toString == NULL) {
        if(1 != InitPerson()){
            return;
        }
    }
    jstring jstr = NULL;
    char* cstr = NULL;
    //调用方法
    jstr = (*jniEnv)->CallObjectMethod(jniEnv, mPerson, toString);
    cstr = (char*) (*jniEnv)->GetStringUTFChars(jniEnv,jstr, 0);
    __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "C JNI toString  ---- >  %s",cstr );
    (*jniEnv)->ReleaseStringUTFChars(jniEnv, jstr, cstr);
    (*jniEnv)->DeleteLocalRef(jniEnv, jstr);
}
 
 

5 资源释放

JNI 基本数据类型是不需要释放的, 如 jint , jlong , jchar 等等 。

我们需要释放是引用数据类型,当然也包括数组家族。如:jstring ,jobject ,jobjectArray,jintArray 等等。

当然,大家可能经常忽略掉的是 jclass ,jmethodID , 这些也是需要释放的

 

Native Code 本身的内存泄漏

JNI 编程首先是一门具体的编程语言,或者 C 语言,或者 C++,或者汇编,或者其它 native 的编程语言。每门编程语言环境都实现了自身的内存管理机制。因此,JNI 程序开发者要遵循 native 语言本身的内存管理机制,避免造成内存泄漏。以 C 语言为例,当用 malloc() 在进程堆中动态分配内存时,JNI 程序在使用完后,应当调用 free() 将内存释放。总之,所有在 native 语言编程中应当注意的内存泄漏规则,在 JNI 编程中依然适应。

Native 语言本身引入的内存泄漏会造成 native memory 的内存,严重情况下会造成 native memory 的 out of memory。

Global Reference 引入的内存泄漏

JNI 编程还要同时遵循 JNI 的规范标准,JVM 附加了 JNI 编程特有的内存管理机制。

JNI 中的 Local Reference 只在 native method 执行时存在,当 native method 执行完后自动失效。这种自动失效,使得对 Local Reference 的使用相对简单,native method 执行完后,它们所引用的 Java 对象的 reference count 会相应减 1。不会造成 Java Heap 中 Java 对象的内存泄漏。

而 Global Reference 对 Java 对象的引用一直有效,因此它们引用的 Java 对象会一直存在 Java Heap 中。程序员在使用 Global Reference 时,需要仔细维护对 Global Reference 的使用。如果一定要使用 Global Reference,务必确保在不用的时候删除。就像在 C 语言中,调用 malloc() 动态分配一块内存之后,调用 free() 释放一样。否则,Global Reference 引用的 Java 对象将永远停留在 Java Heap 中,造成 Java Heap 的内存泄漏。

 

释放String

jstring jstr = NULL;

char* cstr = NULL;

//调用方法

jstr = (*jniEnv)->CallObjectMethod(jniEnv, mPerson, getName);

cstr = (char*) (*jniEnv)->GetStringUTFChars(jniEnv,jstr, 0);

__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "getName  ---->  %s",cstr );

//释放资源

(*jniEnv)->ReleaseStringUTFChars(jniEnv, jstr, cstr);

(*jniEnv)->DeleteLocalRef(jniEnv, jstr);

释放 类 、对象、方法

(*jniEnv)->DeleteLocalRef(jniEnv, XXX);//“XXX” 代表 引用对象

释放 数组家族

jclass jclsStr = (*jniEnv)->FindClass(jniEnv, "java/lang/String");

jobjectArray arrays = (*jniEnv)->NewObjectArray(jniEnv, len, jclsStr, 0);

(*jniEnv)->DeleteLocalRef(jniEnv, jclsStr);  //释放String类

(*jniEnv)->DeleteLocalRef(jniEnv, arrays); //释放jobjectArray数组

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多