NDK开发

现在在android开发中主要使用cmake进行ndk的开发,所以不会再自己创建头文件了。下面我将通过一个例子进行讲解:
java代码:

public class MegfaceAttributeQuality extends MegfaceAttribute {
    static {
        System.loadLibrary("megface-android");
    }
    private native void helloWord(String message);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_megvii_sr_smartretail_attendance_face_MegfaceAttributeQuality_helloWord(JNIEnv *env,
                                                                                 jobject instance,
                                                                                 jstring message_) {
    const char *message = env->GetStringUTFChars(message_, 0);

    // TODO

    env->ReleaseStringUTFChars(message_, message);
}

在java中建立一个native方法,并且用 System.loadLibrary加载这个动态库,在cpp中建立一个相对应的native方法。

环境说明

Java_com_megvii_sr_smartretail_attendance_face_MegfaceAttributeQuality_helloWord(JNIEnv *env,
                                                                                 jobject instance,
                                                                                 jstring message_)

的方法中有三个参数,JNIEnv *env、jobject instance、jstring message_

JNIEnv

JNIEnv类型实际上代表了java环境,通过JNIEnv*指针可以对java端的代码进行操作。例如、创建java类型中的对象,调用java对象的方法,获取java对象的属性等。它是一个桥梁,将java传递过来的参数转换为native可用的类型。例如上例的env->GetStringUTFChars(message_, 0);方法。将java参数转换为成c++可以使用的指针类型,并返回一个char*类型的指针。这里注意最后一个参数:copied,这是一个boolean类型的参数,表示是不是直接将java端所指向的这个参数的指针直接返回,JNI_TRUE表示copy一份,JNI_FALSE表示直接返回java对象的指针。NULL表示不关心是否copy。ReleaseStringUTFChars则表示释放内存,由于在c++中不能自动释放内存,所以必须手动释放,以免内存泄漏。总之,c/c++与java尽心数据传递,方法调用等操作都是通过JNIEnv所定义的方法实现的。

jobject instance

如果native方法不是static,instance就代表native方法的实例。
如果native方法是static,instance就代表native方法的类的class对象实例。

异常处理

在C++和Java的编程中,异常处理都是一个重要的内容。但是在JNI中,麻烦就来了,native方法是通过C++实现的,如果在native方法中发生了异常,如何传导到Java呢?

JNI提供了实现这种功能的机制。我们可以通过下面这段代码抛出一个Java可以接收的异常,

jclass errCls;

env->ExceptionDescribe();

env->ExceptionClear();

errCls = env->FindClass("java/lang/IllegalArgumentException");

env->ThrowNew(errCls, "thrown from C++ code");

如果要抛出其他类型的异常,替换掉FindClass的参数即可。这样,在Java中就可以接收到native方法中抛出的异常。

当JNI遇到多线程

首先明确一点:

JavaVM:是指进程虚拟机环境,每个进程有且只有一个JavaVM实例
JNIEnv:是指线程上下文环境,每个线程有且只有一个JNIEnv实例,

问题描述:

一个java对象通过JNI调用DLL中一个send()函数向服务器发送消息,不等服务器消息到来就立即返回.同时把JNI接口的指针JNIEnv *env,和jobject obj保存在DLL中的变量里.

一段时间后,DLL中的消息接收线程接收到服务器发来的消息,并试图通过保存过的env和obj来调用先前的java对象的方法来处理此消息.

然而,JNI文档上说,JNI接口的指针JNIEnv*不能在c++的线程间共享,在我的程序中,如果接收线程试图调用java对象的方法,程序会突然退出.

不知道有没有方法突破JNI接口的指针不能在多个c++线程中共享的限制?

解决办法:

JNI接口指针不可为多个线程共用,但是java虚拟机的JavaVM指针是整个jvm公用的. 于是,在DLL中可以调用:

static JavaVM* gs_jvm;

env->GetJavaVM(&gs_jvm); 来获取JavaVM指针.获取了这个指针后,在DLL中的另一个线程里,可以调用:

JNIEnv *env;

gs_jvm->AttachCurrentThread((void **)&env, NULL);

来将DLL中的线程 “attached to the virtual machine”(不知如何翻译…),同时获得了这个线程在jvm中的 JNIEnv指针.

由于我需要做的是在DLL中的一个线程里改变某个java对象的值,所以,还必须获取那个java对象的jobject指针.同 JNIEnv 指针一样,jobject指针也不能在多个线程中共享. 就是说,不能直接在保存一个线程中的jobject指针到全局变量中,然后在另外一个线程中使用它.幸运的是,可以用

gs_object=env->NewGlobalRef(obj);

来将传入的obj保存到gs_object中,从而其他线程可以使用这个gs_object来操纵那个java对象了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值