今天,简单讲讲android的jni如何使用jni回调java函数。


之前,我写了部分jni的博客,讲的都是如何从android的java代码调用jni的函数。最近,需要做一个新的功能,在jni的C函数里,需要开一个线程,不停回调java的函数。开始查了很多资料,最终是完成了效果。这里记录一下。


需要调用的java的函数:

/** *  * @param datas * @param dataSize * @param sync_code * @param frametype * @param frameno * @param channel * @param tv_sec * @param tv_msec */public void decodeData(byte[] datas, int dataSize        ,int sync_code,int frametype,int frameno,int channel,int tv_sec,int tv_msec,int hStream) {   }



二.在jni里首先进行回调java函数:

 1.首先定义一个保存变量的结构体。

//记录类相关的信息typedef struct ClassInfo {    JavaVM *jvm;   //保存java虚拟机,这是在新线程中能够回调到java方法的最重要的参数.    jobject obj;   //保存java对象    jmethodID callbackMethodId; //保存methodID    jmethodID receiveDeviceEventId; //保存methodID}ClassInfo;//定义一个全局的ClassInfoClassInfo gClassInfo = {0};



2.对结构体的变量初始化。

JNIEXPORT jint JNICALL Java_com_p2p_protocol_Protocol_1APIs_initCallBack  (JNIEnv *env, jobject obj){    /**         * 说明:jni层如果有多线程,实际上JNIEnv(jni环境变量)是不能够在多线程中共用的, env只能在当前线程有效,         * 但是JavaVM可以,JavaVMJava虚拟机,这个变量是进程可共用的.所以要想在其他线程中回调java方法,需要保存的是jvm.         */        (*env).GetJavaVM(&gClassInfo.jvm);        jclass cls = (*env).FindClass("com/p2p/protocol/Protocol_APIs");        if (NULL == cls) {             LOGE("can't find jclass  ProtocolCallBack");             return -1;        }        gClassInfo.callbackMethodId = (*env).GetMethodID(cls, "decodeData", "([BIIIIIIII)V");        if (NULL == gClassInfo.callbackMethodId) {            LOGE("can't find method ProtocolCallBack from JniClass");            return -1;        }        gClassInfo.receiveDeviceEventId = (*env).GetMethodID(cls, "receiveDeviceEvent", "(II)V");        /**         * 说明:为了能够在其它线程得到java的对象,必须要instance转化为全局对象,这样在其它线程才能得到当前java对象的索引.         * 否则在其它线程要用到当前java对象时,会出现无效引用的错误.         */        gClassInfo.obj = (*env).NewGlobalRef(obj);        if (NULL == gClassInfo.obj) {             LOGE("can't find jobject");             return -1;        }        //调用decodeData方法        //env->CallVoidMethod(gClassInfo.obj,gClassInfo.callbackMethodId,NULL,10);    return 0;  }


说明一下初始化的内容,首先通过GetJavaVM获取到java虚拟机,然后通过FindClass获取调用jni的类,这里必须注意一点,就是这里获取的类只能是调用jni的类,不能是其他类。我调用其他的类,出现崩溃问题。然后通过GetMethodID获取java函数。最后,通过NewGlobalRef新建了回调函数所在的类的实体变量。因为我这里回调的函数不是静态函数,所以需要新建实体类。


这里需要注意java函数变量对应的签名,java函数是

public void decodeData(byte[] datas, int dataSize        ,int sync_code,int frametype,int frameno,int channel,int tv_sec,int tv_msec,int hStream)


对应的获取函数ID的jni是:

gClassInfo.callbackMethodId = (*env).GetMethodID(cls, "decodeData", "([BIIIIIIII)V");

这个函数签名比较重要,也比较复杂,我会在写一遍博客来讲解。


具体的讲解如下:


在本地方法中调用Java对象的方法的步骤:

1)获取你需要访问的Java对象的类

FindClass通过传java中完整的类名来查找java的class

GetObjectClass通过传入jni中的一个java的引用来获取该引用的类型。

他们之间的区别是,前者要求你必须知道完整的类名,后者要求在Jni有一个类的引用。

2)获取MethodID,调用方法

GetMethodID 得到一个实例的方法的ID

GetStaticMethodID 得到一个静态方法的ID

3)获取对象的属性

GetFieldID 得到一个实例的域的ID

GetStaticFieldID 得到一个静态的域的ID

JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数。


3.在线程调用java函数

JNIEnv *env;(*gClassInfo.jvm).AttachCurrentThread(&env, NULL);//调用decodeData方法env->CallVoidMethod(gClassInfo.obj,gClassInfo.callbackMethodId,   jbarray,cFrame->cFrameBuffer.dwBufLen,   header->dwDataPacketStartCode,header->bytFrameType,   header->dwFrameNo,cFrame->dwChannel,header->dwTimestampBySecond,header->dwTimestampByUSecond/1000,hStream);   (*gClassInfo.jvm).DetachCurrentThread();


这里首先获取到线程的JNIEnv,然后通过CallVoidMethod调用java的decodeData函数。

具体的调用函数的代码和函数的返回值相关,对应规则如下:

Instance Method Calling Routines:

CallVoidMethod void
CallObjectMethod jobject
CallBooleanMethod jboolean
CallByteMethod jbyte
CallCharMethod jchar
CallShortMethod jshort
CallIntMethod jint
CallLongMethod jlong
CallFloatMethod jfloat
CallDoubleMethod jdouble


android JNI层线程回调Java函数就讲完了。


就这么简单。

 

更多相关文章

  1. android handler 多线程demo
  2. 硬件访问服务4之Android硬件访问服务框架及系统函数全详细实现
  3. Android下生成core dump的方法
  4. Android 使用Thread+Handler实现非UI线程更新UI界面
  5. ubuntu下eclipse Android ADT中SDK Manager中安装SDK失败的方法
  6. [置顶] Android 从硬件到应用:一步一步向上爬 4 -- 使用 JNI 方法
  7. Android异步加载图像小结(含线程池,缓存方法)[转]
  8. TextView支持的XML属性及相关方法

随机推荐

  1. android studio 获取证书指纹 SHA1
  2. Android事件处理
  3. 自己归纳 Android(安卓)四种布局简介
  4. React Native踩坑:集成到现有Android原生
  5. Android(安卓)的消息队列模型
  6. Android(安卓)WatchDog分析
  7. Android中Activity启动模式详解
  8. Android:RelativeLayout布局的相关属性介
  9. android 布局总结
  10. 用Eclipse开发第一个Android应用程序Hell