标签:
//NDK 使用技巧和多线程调用注意 //http://www.ibm.com/search/csass/search/?q=ndk&sn=dw&lang=zh&cc=CN&en=utf&hpp=20&dws=cndw&lo=zh void demo(JNIEnv* env, jobject thiz) { //这JNI接口指针可以存储,但只在当前线程仍然是有效的。 /* A JNI environment 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.[4] 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); 分离当前线程 自己创建的子线程不能用主线程的JNIEnv了,得用AttachCurrentThread生成自己的JNIEnv ,用完后记得调用DetachCurrentThread。 */ //jni多线程 在底层c++生成的子线程中调用Java,需要JniEnv。而这个JniEnv的生成,需要用AttachCurrentThread方法。 //java字符串 jstring javaString = NULL; // Correct way: Create and release native string from Java string const char *nativeString = (*env)->GetStringUTFChars(env, javaString, 0); printf("%s", nativeString); (*env)->ReleaseStringUTFChars(env, javaString, nativeString); env->DeleteLocalRef(javaString); //java整形数组求和 jintArray arr = NULL; jint buf[10]; jint i, sum = 0; // This line is necessary, since Java arrays are not guaranteed to have a continuous memory layout like C arrays. env->GetIntArrayRegion(arr, 0, 10, buf); for (i = 0; i < 10; i++) { sum += buf[i]; } //程序员在使用 Global Reference 时,需要仔细维护对 Global Reference 的使用。如果一定要使用 Global Reference,务必确保在不用的时候删除。 int JniLoad(JavaVM* jvm, void* reserved) { g_InterfaceObject = env->NewGlobalRef(obj); } void JniUnLoad(JavaVM* jvm, void* reserved) { //全局变量 env->DeleteGlobalRef(g_InterfaceObject); } // jni抛出异常 jclass exceptionClazz = env->FindClass("java/lang/RuntimeException"); //抛出 env->ThrowNew(exceptionClazz, "Unable to find method--"); jclass jcls;//需要DeleteLocalRef jobject jcls;//需要DeleteLocalRef jstring jcls;//需要ReleaseStringUTFChars DeleteLocalRef jarray jcls;//需要DeleteLocalRef jmethodid jfieldid//不需要DeleteLocalRef //当用 malloc() 在进程堆中动态分配内存时,JNI 程序在使用完后,应当调用 free() 将内存释放。 }
http://www.android100.org/html/201308/19/3994.html
一、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 ndk中的工具使用
arm-linux-androideabi-readelf.exe -a XX.so > xx.txt输出所有函数
arm-linux-androideabi-objdump.exe -dx XX.so > xx.txt反汇编so包,此时使用 $(JNI_PROJ_PATH)/obj\local\armeabi下面带符号表的so包。JNI_PROJ_PATH为编译so包时jni文件夹的根目录
arm-linux-androideabi-ar.exe -t xx.a > xx.txt输出.a内所有函数
arm-linux-androideabi-nm.exe xx.a > xx.txt输出.a内所有函数
arm-linux-androideabi-addr2line.exe -f -e E:\dev_code\Sosomap-old\Sosomap-jni\obj\local\armeabi\libXX.so 000263ae
标签:
原文地址:http://blog.csdn.net/earbao/article/details/51340929