ANDROID jni 中的事件回调机制JNIenv的使用

原创 2016年08月31日 12:12:09
android framework 里java调用native,使用JNI机制,java如何调用native,在framework里面的例子很多,有很多参考,可以方便的使用。
但是在一些native中如果涉及到了事件回调,需要在native里调用java对象,在framework 框架里也有这样的例子。
在项目里用到了这一机制。

在native 注册的时候首先保存java的调用方法:
static void 
net_sunniwell_SWProxy_native_init(JNIEnv *env)
{
     LOGE("SWProxy  init\n");

     jclass clazz;
     clazz = env->FindClass("net/sunniwell/media/SWProxy");
     if (clazz == NULL) {
          return;
     }

     fields.post_event = env->GetStaticMethodID(clazz, "postEvent",
               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
     if (fields.post_event == NULL) {
          LOGE("SWProxy  init find postEvent NULL\n");
          return;
     }

     LOGE("SWProxy init find postEvent\n");
}

JNI中调用Java中的方法
void JNISWProxyListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
               msg, ext1, ext2, NULL);
}

这个格式在framework框架中是一个普遍使用的形式。
但是在实际的使用中却经常在env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, NULL)挂掉。

JNIenv 是和线程有关的变量,JVM是和进程有关的变量:在native事件回调中,当调用到notify时,我们获取JNIEnv *env = AndroidRuntime::getJNIEnv(); 但是这个env和当前的线程有没有关联是不能确定的问题,如果native在另外的线程里处理事件回调,这个env就和JNI调用的env共用了。
在正常的JNI调用中JNIENV是由jvm 传递进来的,jni函数的第一个参数就是JNIEnv。如下:
static void 
net_sunniwell_SWProxy_native_finalize( JNIEnv *env )
{
     LOGE("SWProxy  finalize\n");
     http_server_set_eventcallback(0);
}

查阅了一些资料:
There are certain constraints that you must keep in mind when writing native methods that are to run in a multithreaded environment. By understanding and programming within these constraints, your native methods will execute safely no matter how many threads simultaneously execute a given native method. For example:
A JNIEnv pointer is only valid in the thread associated with it. You must not pass this pointer from one thread to another, or cache and use it in multiple threads. The Java virtual machine passes a native method the same JNIEnv pointer in consecutive invocations from the same thread, but passes different JNIEnv pointers when invoking that native method from different threads. Avoid the common mistake of caching the JNIEnv pointer of one thread and using the pointer in another thread.
Local references are valid only in the thread that created them. You must not pass local references from one thread to another. You should always convert local references to global references whenever there is a possibility that multiple threads may use the same reference.

env是线程相关的,env只能在创建它的线程中使用
下面提出了一个解决办法:
env是线程相关的,JVM却是进程相关的。我们可以通过JVM来获取线程相关的JNIENV。
JNIEnv* 
A JNI interface pointer (JNIEnv*) is passed as an argument for each native function mapped to a Java method, allowing for interaction with the JNI environment within the native method. This JNI interface pointer can be stored, but remains valid only in the current thread. Other threads must first call AttachCurrentThread() to attach themselves to the VM and obtain a JNI interface pointer. Once attached, a native thread works like a regular Java thread running within a native method. The native thread remains attached to the VM until it calls DetachCurrentThread() to detach itself. [3] 
//To attach to the current thread and get a JNI interface pointer: 
JNIEnv *env; 
(*g_vm)->AttachCurrentThread (g_vm, (void **) &env, NULL); 
//To detach from the current thread: 
(*g_vm)->DetachCurrentThread (g_vm);

下面是修改以后的代码:

static void 
net_sunniwell_SWProxy_native_init(JNIEnv *env)
{
     LOGE("SWProxy  init\n");

     jclass clazz;
     clazz = env->FindClass("net/sunniwell/media/SWProxy");
     if (clazz == NULL) {
          return;
     }

     fields.post_event = env->GetStaticMethodID(clazz, "postEvent",
               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
     if (fields.post_event == NULL) {
          LOGE("SWProxy  init find postEvent NULL\n");
          return;
     }

     // Set the virtual machine.
     env->GetJavaVM(&(fields.pVM));
     LOGE("SWProxy  init find postEvent\n");
}

void JNISWProxyListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
     JNIEnv *env ;
     fields.pVM->AttachCurrentThread(&env, NULL);
     env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, NULL);
     fields.pVM->DetachCurrentThread();
}

修改以后是可以了,但是却有一个疑问:为什么在framework里面第一种调用方法却是OK的,仔细思考以后,原来和android 的binder机制有关系。binder分为代理端和服务端,在jni中运行的是代理端,当服务端的事件回调被调用时,通过binder跨进程通知代理端,这样代理端还是运行在java JNI 的线程中,所以不会出现JNIenv被多线程共用的情况。
版权声明:本文为博主原创文章,转载请注明出处。

安卓开发——JNI——回调java中的方法

JNI开发中 在C代码中回调java中的方法
  • zl18603543572
  • zl18603543572
  • 2015年09月17日 00:50
  • 1432

直播推流技术及android jni回调java方法总结

》 业内直播推流用2种技术,ffmpeg  推流flv  aac;  librtmp 用adobe公司的非开源库进行推流; 》推流注意问题,音视频同步,推流规格一般是700bps,15fps,vga,...
  • u013316124
  • u013316124
  • 2016年06月30日 22:54
  • 1152

Android NDK开发(五)——C代码回调Java代码

在上篇博客里了解了Java层是怎样传递数据到C层代码,并且熟悉了大部分的实际开发知识,基本上掌握这些就可以做一个基本的NDK开发了,但是光是了解Java回调C层的数据是不是还不够啊,考虑问题要考虑可逆...
  • lee_tianya
  • lee_tianya
  • 2015年01月05日 09:27
  • 5685

android Java与JNI层互相调用

Java层调用JNI中的函数首先在TextJNI.java层创建一个类TextJNI,类中使用一些native方法package com.example.androidjni; import andr...
  • luckywang1103
  • luckywang1103
  • 2015年08月28日 14:25
  • 1982

如何在android的jni线程中实现回调

JNI回调是指在c/c++代码中调用java函数,当在c/c++的线程中执行回调函数时,会导致回调失败。 其中一种在Android系统的解决方案是: 把c/c++中所有线程的创建,由pthread...
  • xnwyd
  • xnwyd
  • 2012年03月16日 11:38
  • 18490

Android Stuido Ndk-Jni 开发(五):Jni回调java静态方法和非静态方法

Android Stuido Ndk-Jni 开发(五):Jni回调java静态方法和非静态方法
  • loque
  • loque
  • 2016年03月03日 14:08
  • 1357

Android JNI中C和JAVA代码之间的互相调用

一、C语言代码回调Java方法 1. C代码回调Java方法的流程 (1) 找到java对应的Class 创建一个char*数组, 然后使用jni.h中提供的FindClass方法获取jclas...
  • quan648997767
  • quan648997767
  • 2017年03月23日 17:07
  • 1722

安卓JNI--JNI底层C回调Java方法

XML布局
  • muyang_ren
  • muyang_ren
  • 2015年12月05日 16:31
  • 2223

获取JNIEnv的方法

static JavaVM *g_JavaVM; g_JavaVM = android::AndroidRuntime::getJavaVM(); static JNIEnv *GetEnv(...
  • asmcvc
  • asmcvc
  • 2015年07月28日 14:08
  • 4883

如何通过JNI传递对象执行回调

JNI的全称是java native interface,用来调用某些特定于系统平台或者硬件的操作,但是它只能调用c/c++的代码,若是其它语言代码,只能通过c/c++进行二次调用。关于JNI的完整技...
  • coutcin
  • coutcin
  • 2006年10月25日 10:19
  • 11134
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:ANDROID jni 中的事件回调机制JNIenv的使用
举报原因:
原因补充:

(最多只允许输入30个字)