ANDROID jni 中的事件回调机制JNIenv的使用 2012-09-10 12:53:01

分类: 嵌入式

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被多线程共用的情况。

ZT ANDROID jni 中的事件回调机制JNIenv的使用 2012-09-10 12:53:01的更多相关文章

  1. Android中的事件分发机制

    Android中的事件分发机制 作者:丁明祥 邮箱:2780087178@qq.com 这篇文章这周之内尽量写完 参考资料: Android事件分发机制完全解析,带你从源码的角度彻底理解(上) And ...

  2. 【Android - 进阶】之事件分发机制

    参考资料: View事件分发:http://blog.csdn.net/pi9nc/article/details/9281829 ViewGroup事件分发:http://blog.csdn.net ...

  3. 【Unity3D技巧】在Unity中使用事件/委托机制(event/delegate)进行GameObject之间的通信 (二) : 引入中间层NotificationCenter

    作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 一对多的观察者模式机制有什么缺点? 想要查看 ...

  4. 安卓中的事件分发机制之View控件

    前言:Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent e ...

  5. 【Unity游戏开发】用C#和Lua实现Unity中的事件分发机制EventDispatcher

    一.简介 最近马三换了一家大公司工作,公司制度规范了一些,因此平时的业余时间多了不少.但是人却懒了下来,最近这一个月都没怎么研究新技术,博客写得也是拖拖拉拉,周六周天就躺尸在家看帖子.看小说,要么就是 ...

  6. JavaScript中的事件循环机制跟函数柯里化

    一.事件循环机制的理解 test();//按秒输出5个5 function test() { for (var i = 0; i < 5; i++) { setTimeout(() => ...

  7. Android开发系列之事件拦截机制

    对于Android开发者来说理解事件传递机制的重要性,我想应该是不言而喻的.在一个Activity里面,我们经常会重写onTouchEvent事件,可是重写结束之后,对于是返回true还是返回fals ...

  8. Android中的事件分发机制总结

    Android 的事件分发机制 一.View的事件分发总结: View的onTouchEvent和OnTouch区别  还是以自定义的TestButton为例. 我们可以通过重写onTouchEven ...

  9. Android jni中回调java的方法

    在上一篇的基础上,添加在C++代码中回调java方法. 代码如下: Demo.java 中添加callback函数, 打印一条log. package com.example.scarecrow.dy ...

随机推荐

  1. html的framset使用

    frameset主要用在显示多个页面的需求下: 看代码: <html> <head> <title>html frameset test</title> ...

  2. Python基础(4) - 变量

    Python 命名规则: 变量名必须是字母或者_开头. 变量名的其他部分可以是字母,_或者数字. Python是大小写敏感的. 以下划线开头的标识符是有特殊意义: 以单下划线开头(_foo)的代表不能 ...

  3. win7(64)+vs2010+opencv2.3.1配置问题:应用程序无法正常启动0xc000007b

    根据:毛星云(浅墨)的[OpenCV入门教程之一] 安装OpenCV:OpenCV 3.0.OpenCV 2.4.8.OpenCV 2.4.9 +VS 开发环境配置 文章链接:http://blog. ...

  4. 深入理解JavaScript系列(39):设计模式之适配器模式

    介绍 适配器模式(Adapter)是将一个类(对象)的接口(方法或属性)转化成客户希望的另外一个接口(方法或属性),适配器模式使得原本由于接口不兼容而不能一起工作的那些类(对象)可以一些工作.速成包装 ...

  5. 判断当前IE浏览器是否支持JS

    1.server 2008 r2 64位中自带的IE默认不支持js,这样一些有JS的页面就是失效,所以如果要考虑这方面的系统,需要判断浏览器是否支持JS <div class="js- ...

  6. 另一个C#模拟post请求的例子

    private string returninstallTmnl(AddTmnlInstallParameter model) { string url = ConfigurationSettings ...

  7. Octotree Chrome安装与使用整理

    Octotree Chrome作用: 主要使你在github查看项目时可以清晰明了的看到项目的结构以及具体代码,使下载代码更具有目的性,减少不必要代码的下载,而且看起来更清楚. 效果图:(安装插件前) ...

  8. 三年从前端小工到架构-知乎 Live 学习整理

    最近在知乎上学习了vczero (王利华,簋谣)的知乎Live「三年从前端小工到架构」,感觉受益匪浅,现将本次Live学习笔记记录如下. 本次 Live 主要包括以下内容   • 0-3 年的前端工程 ...

  9. MyBatis_动态代理

    一.项目结构 二.代码实现 import java.util.List; import java.util.Map; import com.jmu.bean.Student; public inter ...

  10. JS里的居民们4-数组((堆)队列

    编码1(队头在最右) 练习如何使用数组来实现队列,综合考虑使用数组的 push,pop,shift,unshift操作 基于代码,实现如按钮中描述的功能: 实现如阅读材料中,队列的相关入队.出队.获取 ...