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被多线程共用的情况。
- Android中的事件分发机制
Android中的事件分发机制 作者:丁明祥 邮箱:2780087178@qq.com 这篇文章这周之内尽量写完 参考资料: Android事件分发机制完全解析,带你从源码的角度彻底理解(上) And ...
- 【Android - 进阶】之事件分发机制
参考资料: View事件分发:http://blog.csdn.net/pi9nc/article/details/9281829 ViewGroup事件分发:http://blog.csdn.net ...
- 【Unity3D技巧】在Unity中使用事件/委托机制(event/delegate)进行GameObject之间的通信 (二) : 引入中间层NotificationCenter
作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 一对多的观察者模式机制有什么缺点? 想要查看 ...
- 安卓中的事件分发机制之View控件
前言:Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent e ...
- 【Unity游戏开发】用C#和Lua实现Unity中的事件分发机制EventDispatcher
一.简介 最近马三换了一家大公司工作,公司制度规范了一些,因此平时的业余时间多了不少.但是人却懒了下来,最近这一个月都没怎么研究新技术,博客写得也是拖拖拉拉,周六周天就躺尸在家看帖子.看小说,要么就是 ...
- JavaScript中的事件循环机制跟函数柯里化
一.事件循环机制的理解 test();//按秒输出5个5 function test() { for (var i = 0; i < 5; i++) { setTimeout(() => ...
- Android开发系列之事件拦截机制
对于Android开发者来说理解事件传递机制的重要性,我想应该是不言而喻的.在一个Activity里面,我们经常会重写onTouchEvent事件,可是重写结束之后,对于是返回true还是返回fals ...
- Android中的事件分发机制总结
Android 的事件分发机制 一.View的事件分发总结: View的onTouchEvent和OnTouch区别 还是以自定义的TestButton为例. 我们可以通过重写onTouchEven ...
- Android jni中回调java的方法
在上一篇的基础上,添加在C++代码中回调java方法. 代码如下: Demo.java 中添加callback函数, 打印一条log. package com.example.scarecrow.dy ...
随机推荐
- 关于CSS3动画性能
前天我去面试了...好吧,对于自己6年6份工作的悲催经历,我自己也是醉了. 但没办法,我这种当时上学没好好学习,临毕业才出家写代码的半吊子码农,起步没起好,以至于一直没能找到真正让自己满意的工作. 通 ...
- X-Frame-Options配置
因为最近项目需要接入数据统计,其中一项功能需要开启iframe形式来加载页面,所以就开始研究一下iframe如何配置~~~ X-Frame-Options: 他的值有三个: (1)DENY --- 表 ...
- Android OpenGL教程-第二课【转】
第二课 你的第一个多边形: 在第一个教程的基础上,我们添加了一个三角形和一个四边形.也许你认为这很简单,但你已经迈出了一大步,要知道任何在OpenGL中绘制的模型都会被分解为这两种简单的图形. 读完了 ...
- OpenGL初识
OpenGL 概念 OpenGL提供的是一系列接口, 它是指一个规范, OpenGL规范严格规定了每个函数该如何执行, 以及它们的输出值, 具体的实现是由各个显示设备厂商, 它作为本地系统库直接运行在 ...
- React+Three.js——PerspectiveCamera透视相机camera参数以及属性值探索
因项目问题,对webgl进行了探索,当进行到3d相机时,对camera的up,position属性有部分难以理解的地方,因此做下了记录. 代码如下: import React, {Component} ...
- bzoj 2167: 公交车站
Description Z市交通不发达,所有公交路线覆盖的边竟然一个环也不包含,甚至该市的公交路线有可能会分为几个互不连通的块,这可真是不可思议.有一天,你突然听到一条消息,说你的M个同学被困在了Z市 ...
- 架构实战项目心得(五):mysql安装
1. yum安装mysql yum -y install mysql-server 2. 启动mysql服务 启动mysql:service mysqld start 查看mysql的状态:ser ...
- spring整合springmvc和mybatis
1.spring 1.1 jar包 1.2 spring基本配置,包扫描注解 <!-- 自动扫描 --> <context:component-scan base-package=& ...
- maven配置环境
今天初学maven,先学习一下如何在windows下面配置maven,当然你要先配置好jdk的环境. 第一步,上官网下载maven插件,网址是:点击打开链接 第二步,解压文件夹,放在某一个盘符下,我是 ...
- laravel5.7 表单验证
laravel5.7 表单验证 一.创建表单请求 1.找到 larave5.7 文档 验证 中的创建表单请求,这里就要用到命令:php artisan make:request BrandReques ...