zoukankan      html  css  js  c++  java
  • ZT ANDROID jni 中的事件回调机制JNIenv的使用 2012-09-10 12:53:01

    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 ");
     
         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 ");
              return;
         }
     
         LOGE("SWProxy init find postEvent ");
    }
     
    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 ");
         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 ");
     
         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 ");
              return;
         }
     
         // Set the virtual machine.
         env->GetJavaVM(&(fields.pVM));
         LOGE("SWProxy  init find postEvent ");
    }
     
    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被多线程共用的情况。
  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    你会卖掉自己的网上信息吗?大数据可能根本不属于你
    机器学习——TensorFLow实战房价预测
    数据库运作实践三三之歌(秘制口诀)
    1000行MySQL学习笔记,收藏版!
    吐血整理深度学习入门路线及导航【教学视频+大神博客+书籍整理】+【资源页】(2019年已经最后一个月了,你还不学深度学习吗???)
    Ubuntu Snap 简述
    参数传递
  • 原文地址:https://www.cnblogs.com/jeanschen/p/3296467.html
Copyright © 2011-2022 走看看