NDK使用技巧、多线程调用注意、ndk中的工具使用

//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中的工具使用

1. 动态so处理:

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文件夹的根目录

2. 静态a处理:

arm-linux-androideabi-ar.exe -t xx.a > xx.txt

输出.a内所有函数

arm-linux-androideabi-nm.exe xx.a > xx.txt 

输出.a内所有函数

3. ndk编译

上面两种库文件,.a和.so都可以直接通过 arm-linux-androideabi-g++.exe 工具编译,编译语法跟linux上的g++一致。 也可以直接使用ndk-build命令!

4. crash定位

1)使用addr2line将地址转化成代码行数,输入的so为带符号表的,即为strip过的:

arm-linux-androideabi-addr2line.exe -f -e E:\dev_code\Sosomap-old\Sosomap-jni\obj\local\armeabi\libXX.so 000263ae

2)使用ndk-stack.exe还原堆栈:

ndk-stack -sym E:\dev_code\Sosomap-old\Sosomap-jni\obj\local\armeabi -dump D:\android-ndk-r9b-windows-x86\txmap_log.txt

-sym为带符号表的so路径, -dump为crash的堆栈信息,必须包含:********** Crash dump: **********

时间: 2024-08-05 06:33:55

NDK使用技巧、多线程调用注意、ndk中的工具使用的相关文章

[Android初级]NDK入门体验の方法调用

    本文属于自我复习,内容重复请勿见怪 如何在android应用的自定义底层C中调用java的代码?这是本次自我复习的计划. 首先,我得创建一个demo,结构如下: 1.创建一个接口类NativeDataProvider,他的作用是调用c方法,然后回调java方法 public class NativeDataProvider { static final String TAG = "NativeDataProvider"; private Context context; pub

android ndk中的工具使用

1. 动态so处理: 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文件夹的根目录 2. 静态a处理: arm-linux-androideabi-ar.exe

Native Application 开发详解(直接在程序中调用 ntdll.dll 中的 Native API,有内存小、速度快、安全、API丰富等8大优点)

文章目录:                   1. 引子: 2. Native Application Demo 展示: 3. Native Application 简介: 4. Native Application 有何妙用: 5. MJ0011 关于 Native Application 的文章整理: 6. 互联网上其他关于 Native Application 的文章整理: 7. 小结: 1. 引子: 其实在好久以前就看了 MJ0011 翻译的那个<Native 应用程序详细>系列的文

【转】 Pro Android学习笔记(七一):HTTP服务(5):多线程调用HttpClient

目录(?)[-] 应用共享HttpClient对象的同步问题 创建共享HttpClient代码 创建共享对象 创建可共享的HttpClient对象 使用共享HttpClient对象的代码 基础代码 修改HTTP连接的参数 使用共同的Appcliation对象 文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件,转载须注明出处:http://blog.csdn.net/flowingflying/ 应用共享HttpClient对象的同步问题 在之前的例子中,HttpClient只

AngularJS进阶(三十一)AngularJS项目开发技巧之获取模态对话框中的组件ID

AngularJS项目开发技巧之获取模态对话框中的组件ID 需求 出于项目开发需求,须要实现的业务逻辑是:药店端点击查看"已发货""已收货"订单详情时.模块弹出框中仅仅应出现"取消"button.但现实的情况例如以下图所看到的. 模态框核心代码例如以下: <script type="text/ng-template" id="billDtlContent.html"> <div class

IOS异步和多线程操作&amp;&amp;在sqlite3中的应用

1,数据库I/O操作(异步) 数据库本身是存储在磁盘上.访问和修改数据库,即对磁盘进行读写,即I/O操作. 磁盘属于计算机硬件,具有DMA能力,不需要CPU干预,可以实现异步操作. I/O操作一般是消耗时间,sqlite使用异步处理I/O操作. 当有多个事务对数据库进行操作,对应,也会有多个I/O操作. 操作系统将I/O操作,合理放入一个I/O队列.一次性将队列内的I/O操作提交给磁盘系统,并行处理多个I/O,提高效率.本人也没有特别深入研究. 2,异步和多线程 异步和多线程,都有能力实现,不阻

浅谈多线程在java程序中的应用

在一个高并发的网站中,多线程是必不可少的.下面先说一下多线程在程序中的作用.1.提高前端请求的响应速度.当我们执行一个比较耗时的方法时,http请求得不到响应甚至会超时,这时如果业务上允许数据的延迟,我们可以使用多线程来进行处理比较耗时的方法.这样前端发送了请求,后端令开启了一个线程去处理任务,就不会阻塞主线程了.2.减清服务器的压力.包括我们的web容器,如tomcat.jetty等,还有数据库服务器等.因为我们使用了多线程,并且线程池大小有限制,如30,那么同时请求数据库的链接就限制为30了

Java多线程实现的4中方式

对于所有语言来说,多线程的编程是绝不可少的.同样的Java语言也包含了多线程的开发.首先,我们先来了解一下Java语言的多线程实现方式. 一.Java 多线程实现方式 java中实现多线程的方式有三种,接下来我将会逐个进行介绍. 1.继承Thread类 继承Thread类是Java中比较常见,也是很基础的一种实现Java多线程的方式.实现的方式也比较简单,只要将需要实现多线程的Java类继承java.lang.Thread类即可. class MyThread extends Thread{ p

python-多线程:调用thread模块中的start_new_thread()函数来产生新线程

Python 多线程 多线程类似于同时执行多个不同程序,多线程运行有如下优点: 使用线程可以把占据长时间的程序中的任务放到后台去处理. 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度 程序的运行速度可能加快 在一些等待的任务实现上如用户输入.文件读写和网络收发数据等,线程就比较有用了.在这种情况下我们可以释放一些珍贵的资源如内存占用等等. 线程在执行过程中与进程还是有区别的.每个独立的线程有一个程序运行的入口.顺序执行序列和程序的出口.