• (1)Java传到JNI,使用GetByteArrayRegion的方式: 该
    方法的本质是将Java端数组数据拷贝到本地的数组中,所以在JNI对数据修改后Java端的数据并没
    有改变。 使用GetPrimitiveArrayCritical。 GetPrimitiveArrayCritical 表面上可以得到底层数据指针,
    在JNI层修改数组时Java层的数据也会变。But,如果只使用GetPrimitiveArrayCritical获取数据,
    程序运行一段时间内存会crash。所以,使用GetPrimitiveArrayCritical时必须使用
    ReleasePrimitiveArrayCritical ,通过测试发现当数据量大时执行ReleasePrimitiveArrayCritical会非
    常耗时。

    (2)JNI传到Java: 把Jni层的数组传递到Java层,一般有两种方法,一种是通过native
    函数的返回值来传递,另一种是通过jni层回调java层的函数来传递,后者多用于jni的线程中或是
    数据量较大的情况。无论哪种方法,都离不开 SetByteArrayRegion 函数,该函数将本地的数组数
    据拷贝到了 Java 端的数组中。 请注意,上面的方式都涉及到内存复制,根据实战经验,在
    Android系统中,一旦数据量变大,拷贝一次内存将非常耗时。所以上述方式在追求效率时不推
    荐使用。解决的方法可以尝试让JAVA层和JNI共享内存的方式。最后找到了两种方式。

    unsigned char* psrcImg = (unsigned char*)(env->GetByteArrayElements(srcImg,0));
    unsigned char* pBufferI420 = (unsigned char*) (env->GetByteArrayElements(dstImg,0));
    if (psrcImg == NULL || pBufferI420 == NULL)
    {
    return -1;
    }
    env->ReleaseByteArrayElements(srcImg,(jbyte*)psrcImg,0);
    env->ReleaseByteArrayElements(dstImg,(jbyte*)pBufferI420,0);123456789
    JNIEnv *env ;
    if (savedVm->AttachCurrentThread(&env, 0) != 0)
    {
    LOGI("Failed to attach current thread");
    return;
    }
    //第一个参数是数据,类型为void*,第二个为数据大小
    jobject buf = env->NewDirectByteBuffer(frame_mjpeg->data, frame_mjpeg->actual_bytes);
    //调起Java的方法,作为参数传入
    env->CallVoidMethod(mObject, on_new_frame_method_id,buf);
    env->ExceptionClear();
    env->DeleteLocalRef(buf);
    savedVm->DetachCurrentThread();12345678910111213
    

    使用共享内存的方式

    (1)使用GetByteArrayElements方式 该方式是指针的形式,将本地的数组
    指针直接指向Java端的数组地址,其实本质上是JVM在堆上分配的这个数组对象上增加一个引用
    计数,保证垃圾回收的时候不要释放,从而交给本地的指针使用,使用完毕后指针一定要记得通
    过ReleaseByteArrayElements进行释放,否则会产生内存泄露。

    (2)使用Direct Buffer 方式传递。 Java和Jni层的数组传递还有一个比较重要的方式,就是通过
    Direct Buffer来传递,这种方式类似于在堆上创建创建了一个Java和Jni层共享的整块内存区域,无
    论是Java层或者Jni层均可访问这块内存,并且Java端与Jni端同步变化,由于是采用的是共享内存
    的方式,因此相比于普通的数组传递,效率更高,但是由于构造/析构/维护这块共享内存的代价
    比较大,所以小数据量的数组建议还是采用上述方式,Direct Buffer方式更适合长期使用频繁访问
    的大块内存的共享。具体可使用GetDirectBufferAddress获得共享的内存地址。 比如在涉及到
    RTSP获取预览图像数据的时候,需要一帧一帧的发送到Java,就可以使用这种方法:
    在JNI层,尽量避免使用内存拷贝。

    上一篇:
    分区存储
    下一篇:
    什么是JNI?它主要用来干什么。
    本文目录
    本文目录