一、jni调用java对象
    JNI提供的功能之一是在本地代码中使用Java对象。包括:创建一个java类对象和通过函数传递一个java对象。创建一个java类对象,首先需要得到得到使用FindClass/GetObjectClass函数得到该类,然后使用GetMethodID方法得到该类的方法id,然后调用该函数。 Java 和 Native 代码之间函数调用时,如果是简单类型,也就是内置类型,比如 int, char 等是值传递(pass by value),而其它 Java 对象都是引用传递(pass by reference),这些对象引用由 JVM 传给 Native 代码。
在本地方法中调用Java对象的方法的步骤:
1)获取你需要访问的Java对象的类
FindClass通过传java中完整的类名来查找java的class
GetObjectClass通过传入jni中的一个java的引用来获取该引用的类型。
他们之间的区别是,前者要求你必须知道完整的类名,后者要求在Jni有一个类的引用。
2)获取MethodID,调用方法
GetMethodID 得到一个实例的方法的ID 
GetStaticMethodID 得到一个静态方法的ID 
3)获取对象的属性
GetFieldID 得到一个实例的域的ID 
GetStaticFieldID 得到一个静态的域的ID
JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数。
 
二、jni中引用的java对象的生命周期
Java对象做为引用被传递到本地方法中,所有这些Java对象的引用都有一个共同的父类型jobject(相当于java中的 Object类是所有类的父类一样)。 这些对象引用都有其生命周期。在JNI中对Java对象的引用根据生命周期分为:全局引用,局部引用、弱全局引用
1、Local Reference 本地引用,
函数调用时传入jobject或者jni函数创建的jobejct,都是本地引用.
其特点就是一旦JNI层函数返回,jobject就被垃圾回收掉,所以需要注意其生命周期。可以强制调用DeleteLocalRef进行立即回收。
 jstring pathStr = env->NewStringUTF(path)
 ....
 env->DeleteLocalRef(pathStr);
2、Global Reference 全局引用 ,这种对象如不主动释放,它永远都不会被垃圾回收
 创建: env->NewGlobalRef(obj);
 释放: env->DeleteGlobalRef(obj)
 若要在某个 Native 代码返回后,还希望能继续使用 JVM 提供的参数, 或者是过程中调用 JNI 函数的返回值(比如 g_mid), 则将该对象设为 global reference,以后只能使用这个 global reference;若不是一个 jobject,则无需这么做。
3、Weak Global Reference 弱全局引用
一种特殊的 Global Reference ,在运行过程中可能被垃圾回收掉,所以使用时请务必注意其生命周期及随时可能被垃圾回收掉,比如内存不足时。
 使用前可以利用JNIEnv的 IsSameObject 进行判定它是否被回收
 env->IsSameObject(obj1,obj2);
 
三、本地线程中调用java对象
问题1:
JNIEnv是一个线程相关的变量
JNIEnv 对于每个 thread 而言是唯一的 
JNIEnv *env指针不可以为多个线程共用
解决办法:
但是java虚拟机的JavaVM指针是整个jvm公用的,我们可以通过JavaVM来得到当前线程的JNIEnv指针.
可以使用javaAttachThread保证取得当前线程的Jni环境变量
static JavaVM *gs_jvm=NULL;
gs_jvm->AttachCurrentThread((void **)&env, NULL);//附加当前线程到一个Java虚拟机
jclass cls = env->GetObjectClass(gs_object);
jfieldID fieldPtr = env->GetFieldID(cls,"value","I");
问题2:
不能直接保存一个线程中的jobject指针到全局变量中,然后在另外一个线程中使用它。
解决办法:
用env->NewGlobalRef创建一个全局变量,将传入的obj(局部变量)保存到全局变量中,其他线程可以使用这个全局变量来操纵这个java对象
注意:若不是一个 jobject,则不需要这么做。如:
jclass 是由 jobject public 继承而来的子类,所以它当然是一个 jobject,需要创建一个 global reference 以便日后使用。
而 jmethodID/jfieldID 与 jobject 没有继承关系,它不是一个 jobject,只是个整数,所以不存在被释放与否的问题,可保存后直接使用。
static jobject gs_object=NULL;
JNIEXPORT void JNICALL Java_Test_setEnev(JNIEnv *env, jobject obj)
{
    env->GetJavaVM(&gs_jvm); //保存到全局变量中JVM 
    //直接赋值obj到全局变量是不行的,应该调用以下函数: 
    gs_object=env->NewGlobalRef(obj);
}
 
jni部分代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<jni.h>
#include<android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "native-activity", __VA_ARGS__))
 
//全局变量
JavaVM *g_jvm = NULL;
jobject g_obj = NULL;
void *thread_fun(void* arg)
 {
     JNIEnv *env;
     jclass cls;
     jmethodID mid;
 
     //Attach主线程
     if((*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL) != JNI_OK)
     {
         LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
         return NULL;
     }
     //找到对应的类
     cls = (*env)->GetObjectClass(env,g_obj);
     if(cls == NULL)
     {
         LOGE("FindClass() Error.....");
         goto error;
     }
     //再获得类中的方法
     mid = (*env)->GetMethodID(env, cls, "fromJNI", "(I)V");
     if (mid == NULL)
     {
         LOGE("GetMethodID() Error.....");
         goto error; 
     }
     //最后调用java中的静态方法
         (*env)->CallVoidMethod(env, cls, mid ,(int)arg);
   
 
 error:   
     //Detach主线程
     if((*g_jvm)->DetachCurrentThread(g_jvm) != JNI_OK)
     {
         LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
     }
    
 
     pthread_exit(0);
 }
 
 //由java调用以创建子线程
 JNIEXPORT void Java_com_test_JniThreadTestActivity_mainThread( JNIEnv* env, jobject obj, jint threadNum)
 {
     int i;
     pthread_t* pt;
     pt = (pthread_t*) malloc(threadNum * sizeof(pthread_t));
     for (i = 0; i < threadNum; i++){
         //创建子线程
        pthread_create(&pt[i], NULL, &thread_fun, (void *)i);
}
 
for (i = 0; i < threadNum; i++){
pthread_join (pt[i], NULL);
}
LOGE("main thread exit.....");
}
 
 
//由java调用来建立JNI环境
JNIEXPORT void Java_com_test_JniThreadTestActivity_setJNIEnv( JNIEnv* env, jobject obj)
 {
     //保存全局JVM以便在子线程中使用
     (*env)->GetJavaVM(env,&g_jvm);
     //不能直接赋值(g_obj = obj)
     g_obj = (*env)->NewGlobalRef(env,obj);
 }
 
 
 //当动态库被加载时这个函数被系统调用
 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
 {
     JNIEnv* env = NULL;
     jint result = -1;   
 
     //获取JNI版本
     if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK)
     {
         LOGE("GetEnv failed!");
             return result;
     }
 
     return JNI_VERSION_1_4;
 }
需要全部源码的,可以打开这个链接下载
http://download.csdn.net/detail/mfcai_blog/5772377
 
本文欢迎转载,转载请注明出处与作者
出处:http://blog.sina.com.cn/staratsky
作者:流星

深入了解android平台的jni---本地多线程调用java代码的更多相关文章

  1. Jni本地多线程回调Java函数,env->findClass()失败。

    遇到的问题,Native层本地多线程回调Java函数时env->findClass()失败. 前面的代码是这样的在 JNI_OnLoad记录全局变量g_vm static JavaVM* g_v ...

  2. Android平台下使用lua调用Java代码经验总结

    动态语言以其执行的灵活性,可配置性.方便调试能够为开发带来极大的方便.假设用好了.能够极大的提高开发的效率. 怪不得像游戏开发这样复杂的软件开发里没有不集成脚本语言的. 当中,lua以其小巧,灵活.方 ...

  3. Android与JNI(三) ---- c++调用java(转载)

    源码下载:JniDemo JNI就是Java Native Interface, 即可以实现Java调用本地库, 也可以实现C/C++调用Java代码, 从而实现了两种语言的互通, 可以让我们更加灵活 ...

  4. Android JNI c/c++调用java 无需新建虚拟机

    近期通过研究SDL源码 得出android JNI  c/c++调用java 无需新建虚拟机: 具体步骤如下 第一步获得:两个参数 JNIEnv和jclass void Java_com_Test_A ...

  5. Android JNI中C调用Java方法

    背景需求 我们需要在JNI的C代码调用Java代码.实现原理:使用JNI提供的反射借口来反射得到Java方法,进行调用. JNI关键方法讲解. 1. 在同一个类中,调用其他方法 JNIEXPORT v ...

  6. cocos2d-x笔记5: 通过jni实现C++调用Java

    Cocos2d-x的跨平台性很强大,但是偶尔也需要平台的原生API结合. C++在Win32平台下简单的很,C++可以直接用MFC或者调用Win32API. Ios在XCode下直接就能C++和OC混 ...

  7. 解析android framework下利用app_process来调用java写的命令及示例

    解析android framework下利用app_process来调用java写的命令及示例 在android SDK的framework/base/cmds目录下了,有不少目录,这些目的最终都是b ...

  8. JNI NDK (AndroidStudio+CMake )实现C C++调用Java代码流程

    JNI/NDK Java调用C/C++前言  通过第三篇文章讲解在实际的开发过程中Java层调用C/C++层的处理流程.其实我们在很大的业务里也需要C/C+ +层去调用Java层,这两层之间的相互调用 ...

  9. JNI中C调用Java方法

    背景需求 我们需要在JNI的C代码调用Java代码.实现原理:使用JNI提供的反射借口来反射得到Java方法,进行调用. JNI关键方法讲解. 1. 在同一个类中,调用其他方法 JNIEXPORT v ...

随机推荐

  1. [Asp.net MVC]Asp.net MVC5系列——Razor语法

    Razor视图引擎是Asp.net MVC3中新扩展的内容,并且也是它的默认视图引擎.还有另外一种Web Forms视图引擎.通过前面的文章可知在Asp.net mvc5中创建视图,默认使用的是Raz ...

  2. 30+简约时尚的Macbook贴花

    当Macbooks Pro电脑在他们的设计之下仍然漂亮.独一无二时,我想说,他们已经成为相当的主流了.有时候如果你回忆过去的很美好的日子,当人们偷偷欣赏你的技术装备 的时候,大概是为你的外表增加亮点的 ...

  3. flex之组件简单应用

    1. 将父窗体的内容传递给子窗体: 2.将子窗体内容传递给父窗体:即用户名和密码,t1是父窗体文本框id.

  4. ZOJ-3201 Tree of Tree 树形DP

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3201 题意:给一颗树,每个节点有一个权值,求节点数为n的最大权子 ...

  5. c# 范型Dictionary实用例子

    //定义 public static Dictionary<string, object> dict =new Dictionary<string, object>(); // ...

  6. 第三百三十四天 how can I 坚持

    I give up my dream that day,else,I coming on,the day my heart is die…… 那天,梦已碎,那天,心已死. 晚上看了个电影<奔爱& ...

  7. REST四种请求(get,delete,put,post) 收集整理 之一

    转自:http://blog.csdn.net/cloudcraft/article/details/10087033 资源是REST中最关键的抽象概念,它们是能够被远程访问的应用程序对象.一个资源就 ...

  8. ms-class的进化

    ms-class是avalon用得最多的几个绑定之一,也正因为如此其功能一直在扩充中.根据时期的不同,分为旧风格与新风格两种. 旧风格是指正在ms-class后面跟着类外,然后在绑定值中添加表达式,即 ...

  9. hdu 1576 A/B (扩展欧几里德简单运用)

    http://acm.hdu.edu.cn/showproblem.php?pid=1576 A/B Time Limit: 1000/1000 MS (Java/Others) Memory Lim ...

  10. E3-1230和E3-1230 V2有多神?

    最近追E3-1230,枪E3-1230的人那叫一个多啊,都被捧成神了,我也来说说对E3-1230的看法.同档次的装机方案,我更倾向i5 2320/2500K/3570K. 首 先比较两个U的规格吧.E ...