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中的方法

(转载)ANDROID jni 中的事件回调机制JNIenv的使用

原文地址:http://blog.chinaunix.net/uid-21564437-id-3343209.html android framework 里java调用native...

获取JNIEnv的方法

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

jni 新线程使用JNIEnv *env

众所周知JNIEnv env 是一个线程对应一个env,线程间不可以共享同一个env变量。那么如何在新创建的线程中使用env变量呢? 1、JavaVM *g_vm;        env->Ge...

JNI学习积累之三 ---- 操作JNI函数以及复杂对象传递

本文原创,转载请注明出处:http://blog.csdn.net/qinjuning          在掌握了JNI函数的使用和相关类型的映射后,以及知晓何利用javah...

定制Android系统开发之八——实现从JNI到Java的回调

前面已经实现了APP->xxxManager->xxxManagerService->jni的函数调用,这篇博文就来实现jni->xxxManagerService的回调。...
  • wtianok
  • wtianok
  • 2015年11月02日 16:54
  • 942

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

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

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

JNI实现回调| JNI调用JAVA函数|参数和返回值的格式

代码下载:http://dl.dbank.com/c0c0xs3b24 一、JNI实现回调 通过JNI在Native层调用JAVA层的方法,来实现Native层向JAVA层传递消息。 JNI...
  • stefzeus
  • stefzeus
  • 2011年08月26日 11:22
  • 19449

Android App层通过JNI从驱动获取Input Event

1 概述   尝试在App层直接读取驱动的Input Event,获取触屏事件(本文获取的是电磁笔触屏事件),而不通过Android的Input Framework.     2 架构 3 ...
  • xnwyd
  • xnwyd
  • 2014年12月29日 11:13
  • 7226
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:ANDROID jni 中的事件回调机制JNIenv的使用
举报原因:
原因补充:

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