JNI C/C++ Callback from JAVA

Posted 跨链技术践行者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JNI C/C++ Callback from JAVA相关的知识,希望对你有一定的参考价值。

JNI 是 JAVA世界和底层世界沟通的重要桥梁,在android的底层开发中用到的较多。相关知识在网络上也容易搜索到,这里也不再累述。
本篇文章,要介绍的是,在Java通过JNI传递一个callback函数到C/C++中。

JAVA层

首先你需要有个callback 的类:

public interface BLDeviceInfoCallback 
    String deviceInfo(String did, String name, boolean state);

然呢在你提供生成JNI的JAVA类里面,添加相应的native函数:

可以使用javah生成相应的jni的.h文件,类似于

NIEXPORT void JNICALL Java_cn_com_broadlink_vtbridge_VtBridegeApi_setBlDeviceInfoCallback(JNIEnv *, jobject, jobject)

C/C++层

根据刚才生成的jni .h文件,我们再去实现相应的接口:

static JavaVM *gJavaVM;
static jobject gInfoObject;
static jmethodID gInfoMethodID;
JNIEXPORT void JNICALL Java_cn_com_broadlink_vtbridge_VtBridegeApi_setBlDeviceInfoCallback(JNIEnv *env, jobject thiz, jobject jCallback) 
    if (gJavaVM == NULL) 
        (*env)->GetJavaVM(env, &gJavaVM);
        if (gJavaVM == NULL) 
            loge("Get gJavaVM is NULL");
            return;
        
    
    gInfoObject = (*env)->NewGlobalRef(env, jCallback);
    if (gInfoObject == NULL) 
        loge("Get gInfoObject failed");
        return;
    
    jclass infoClass = (*env)->GetObjectClass(env, jCallback);
    if (infoClass == NULL) 
        loge("Get infoClass failed");
        return;
    
    gInfoMethodID = (*env)->GetMethodID(env, infoClass, "deviceInfo", "(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;");
    if (gInfoMethodID == 0 ) 
        loge("Cannot find method:vtDeviceControl");
        return;
    

如上就将JAVA函数映射到了C里面,但是由于env 环境是线程不安全的,所以我们获取了唯一JavaVM 虚拟机来实现线程中调用相应类和方法。

static JNIEnv *Adapter_GetEnv() 
    int status;
    JNIEnv *envnow = NULL;
    if (gJavaVM == NULL) 
        loge("JavaVM is NULL");
        return NULL;
    
    status = (*gJavaVM)->GetEnv(gJavaVM,(void **)&envnow, JNI_VERSION_1_4);
    if(status < 0)
    
        status = (*gJavaVM)->AttachCurrentThread(gJavaVM, &envnow, NULL);
        if(status < 0)
        
            return NULL;
        
        g_bAttatedT = true;
    
    return envnow;


static void DetachCurrent() 
    if(g_bAttatedT)
    
        (*gJavaVM)->DetachCurrentThread(gJavaVM);
    

然后实现C函数,根据上述获取到的JAVA虚拟机,类ID和方法ID,来调用JAVA层实现的函数

int vtbridge_device_info(unsigned char *did, unsigned char *name, bool state, unsigned char *output, int outLen) 
    char *cstr = NULL;
    if (NULL == did || NULL == name || NULL == output) 
        loge("vtbridge_device_info input error");
        return -1;
    
    JNIEnv *env = Adapter_GetEnv();
    if (env) 
        jstring jDid = (*env)->NewStringUTF(env, (const char *)did);
        jstring jName = (*env)->NewStringUTF(env, (const char *)name);
        jstring jresult = (*env)->CallObjectMethod(env, gInfoObject, gInfoMethodID, jDid, jName, state);
        cstr = (char *)(*env)->GetStringUTFChars(env, jresult, 0);
        strncpy(output, cstr, outLen);
        if (NULL != cstr) 
            (*env)->ReleaseStringUTFChars(env, jresult, cstr);
        
        DetachCurrent();
        return 0;
    
    return -1;

至此在提供的JNI库中就完成了C调用JAVA callback函数的工作,而在应用层中,只需要在相应的代码中实现

public interface BLDeviceInfoCallback 
    String deviceInfo(String did, String name, boolean state);

就可以了。

注意事项:

1.  GetMethodID CallXXXXMethod 以及 GetStaticMethodID CallStaticXXXXMethod 需要根据实际的JAVA方法进行区分,由于在老版本Android平台上上述方法区分不明显,所以可能导致在老版本Android上跑的正常,而到了新版本Android平台上则报错;
2.  同样由于有些方法是类方法,有些是实例方法,所以在调用CallXXXXMethod和CallStaticXXXXMethod,需要区分清楚具体的参数是jclass还是jobject;

以上是关于JNI C/C++ Callback from JAVA的主要内容,如果未能解决你的问题,请参考以下文章

JNI/NDK开发指南——C/C++访问Java实例方法和静态方法

JNI签名与数据匹配

jni调用 java和c是同个线程吗

JNI/NDK开发指南—— JNI开发流程及HelloWorld

混合编程jni 第十篇之JNA初见

Android JNI编程—JNI基础