文档:Java Native Interface Specification
http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html
什么是JNI?
Java Native Interface (JNI). The JNI is a native programming interface. It allows Java code that runs inside a Java Virtual Machine (VM) to interoperate with applications and libraries written in other programming languages, such as C, C++, and assembly.
JNI是本地编程接口,它允许java应用程序和C/C++/asm的库在java虚拟机上交互。
搭建开发环境
下载eclips
http://blog.csdn.net/data_backups/article/details/48004943
根据PC机的平台下载其中的
ADT Bundle
里面包含了:sdk + 特定版本platform + eclipse + adt + 兼容包, 解压缩即可使用。
开发jni需要安装NDK工具
为什么eclips中编译JNI要安装NDK?
NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。
NDK介绍:
http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html
工具下载:
Android NDK
http://blog.csdn.net/data_backups/article/details/48004847
NDK工具自带的hello-jni的例子
Hello-jni:http://download.csdn.net/detail/data_backups/9050683
下载后把hello-jni工程导入eclips中去,并把NDK集成到eclips中。
集成NDK开发环境
http://blog.csdn.net/data_backups/article/details/47809201
OK一切准备就绪,跑一下试试。
(图一)
代码分析
(图二)
结构上看比正常开发apk多了一个jni目录,Java可以调用JNI的接口。
(图三)
代码:
HelloJni.java
public class HelloJni extends Activity
{
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
/* Create a TextView and set its content.
* the text is retrieved by calling a native
* function.
*/
TextView tv = new TextView(this);
tv.setText( stringFromJNI() );
setContentView(tv);
}
/* A native method that is implemented by the
* 'hello-jni' native library, which is packaged
* with this application.
*/
public native String stringFromJNI();
/* This is another native method declaration that is *not*
* implemented by 'hello-jni'. This is simply to show that
* you can declare as many native methods in your Java code
* as you want, their implementation is searched in the
* currently loaded native libraries only the first time
* you call them.
*
* Trying to call this function will result in a
* java.lang.UnsatisfiedLinkError exception !
*/
public native String unimplementedStringFromJNI();
/* this is used to load the 'hello-jni' library on application
* startup. The library has already been unpacked into
* /data/data/com.example.hellojni/lib/libhello-jni.so at
* installation time by the package manager.
*/
static {
System.loadLibrary("hello-jni");
}
}
如果java要调用native函数,就必须通过一个位于JNI层的动态库来实现,那么在调用这个native函数前必须加载这个动态库。通常是在类的static语句中加载,调用System.loadLibrary方法就可以了,加载时只需要调用库的名字,系统会自动扩展成hello-jni.so。
如何调用呢?在java中函数声明前面带有native的代表此函数是jni层实现的代码,java中只要声明就可以使用了。
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
#Jni的文件编译成动态库就好,名字无所谓
Application.mk
APP_ABI := all
编译成那个平台的动态库,有armeabi-v7a/NEON (hard-float), armeabi-v7a/NEON, armeabi-v7a (hard-float), armeabi-v7a, armeabi, x86, x86_64, mips64, mips, arm64-v8a这些,根据自己的平台替换all以防止编译出全部的动态库,一般arm平台的用armeabi-v7a或armeabi就可以。
hello-jni.c
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI.");
}
调用Java层的Native函数时,如何能调用到JNI层对应的函数呢,这里有两种方法调用,静态方法和动态方法,NDK的例子显然是用了静态方法注册的。
使用javah生成jni头文件,javah的用法如下:
C:\Users\slz\Desktop>javah --help
用法:
javah [options]
其中, [options] 包括:
-o
-d
-v -verbose 启用详细输出
-h --help -? 输出此消息
-version 输出版本信息
-jni 生成 JNI 样式的标头文件 (默认值)
-force 始终写入输出文件
-classpath
-bootclasspath
(例如, java.lang.Object)。
正常应该是
javah -classpath 路径 -jni 包名.类名
但我怎么做也不成功,索性就到工程的src目录下直接执行:
Javah -d 目录 包名.类名
D:\BaiduYunDownload\android-ndk32-r10-windows-x86\android-ndk-r10\samples\hello-jni\src>javah -d C:\Users\slz\Desktop com.example.hellojni.HelloJni
头文件就直接生成在了桌面上
com_example_hellojni_HelloJni.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_example_hellojni_HelloJni */
#ifndef _Included_com_example_hellojni_HelloJni
#define _Included_com_example_hellojni_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_hellojni_HelloJni
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI
(JNIEnv *, jobject);
/*
* Class: com_example_hellojni_HelloJni
* Method: unimplementedStringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
可见带有Native声明的函数都生成到了jni的头文件中。
如何建立关系是由虚拟机完成的,原理大概是java层调用stringFromJNI() 函数时,它会从对应的JNI库中寻找Java_com_example_hellojni_HelloJni_stringFromJNI()函数,如果没有就会报错,如果找到,则会为这两个函数建立一个关联关系,这样以后再调用stringFromJNI()时就直接去找建立好关系的JNI的函数指针就可以了。
静态方法写起来一大串,用起来也不灵活。
写一个简单的动态注册的例子
代码下载:http://download.csdn.net/detail/data_backups/9053917
hello-jni.c
#include
#include
jstring
stringFromJNI_native( JNIEnv* env, jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI dynamic registration!");
}
/**
* 方法对应表
*/
static JNINativeMethod gMethods[] = {
{"stringFromJNI", "()Ljava/lang/String;", (void*)stringFromJNI_native},
};
/*
* 为某一个类注册本地方法
*/
static int registerNativeMethods(JNIEnv* env , const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
**为所有类注册本地方法
*/
static int registerNatives(JNIEnv* env) {
const char* kClassName = "com/example/hellojni/HelloJni";//指定要注册的类
return registerNativeMethods(env, kClassName, gMethods,
sizeof(gMethods) / sizeof(gMethods[0]));
}
/*
* * System.loadLibrary("lib")时调用 如果成功返回JNI版本, 失败返回-1
*/
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
jint result = -1;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
if (!registerNatives(env)) {
return -1;
}
result = JNI_VERSION_1_4;
return result;
}
代码分析:
System.loadLibrary()加载JNI动态库后会查找JNI_Onload的函数,JNI_Onload会将JavaVM指针传递进来,透过JavaVM指针取得JNIEnv指针,并存在env 变量中,然后调用clazz = (*env)->FindClass(env, className);
(*env)->RegisterNatives(env, clazz, gMethods, numMethods)
来完成JNI本地函数和java声明的native函数的一一对应。
运行一下
(图四)
动态注册的疑惑比较多
JavaVM JNIEnv JNINativeMethod Jobject都是什么?
FindClass() RegisterNatives() "()Ljava/lang/String;"又是什么?
Java语言执行在java虚拟机JVM的环境中,JVM是主机环境中的一个进程,每个JVM虚拟机进程在本地环境中都有一个JavaVM结构体,该结构体在创建Java虚拟机时被返回。
JNIEnv是当前java线程的执行环境,一个JVM对应一个JavaVM结构体,而一个JVM中可能创建多个java线程,每个线程对应一个JNIEnv结构,因此不同的线程的JNIEnv不同,也不能相互共享使用。
(图五)
当Java代码调用本地方法时,VM将JNI接口指针作为参数传递给本地方法,本地方法有可以通过JNI的函数表找到与java对应的方法。
(图六)
JNIEnv实际上是提供了一些JNI系统函数,通过这些系统函数可以做到调用java函数,操作jobject对象等很多事情。
Java对象的数据类型在JNI中都用jobject表示,hello-jni例子中stringFromJNI_native( JNIEnv* env, jobject thiz )中的thiz就代表了java层的HelloJni对象,它表示是在哪个HelloJni对象上调用的stringFromJNI,如果Java层是static函数,那么这个参数将是jclass,表示是在调用哪个Java Class的静态函数。
Java调用JNI函数传递的是java数据类型,这些类型到了JNI层该如何表示?
Primitive Types 基础类型
Primitive Types and Native Equivalents
Java Type |
Native Type |
Description |
boolean |
jboolean |
unsigned 8 bits |
byte |
jbyte |
signed 8 bits |
char |
jchar |
unsigned 16 bits |
short |
jshort |
signed 16 bits |
int |
jint |
signed 32 bits |
long |
jlong |
signed 64 bits |
float |
jfloat |
32 bits |
double |
jdouble |
64 bits |
void |
void |
N/A |
The following definition is provided for convenience.
#define JNI_FALSE 0 #define JNI_TRUE 1
The jsize integer type is used to describe cardinal indices and sizes:
typedef jint jsize;
Reference Types 引用类型
The JNI includes a number of reference types that correspond to different kinds of Java objects
Java Type |
Native Type |
All objects |
jobject |
java.lang.Class object |
jclass |
java.lang.String object |
jstring |
object[] |
jobjectArray |
boolean[] |
jbooleanArray |
byte[] |
jbyteArray |
char[] |
jcharArray |
short[] |
jshortArray |
int[] |
jintArray |
long[] |
jlongArray |
float[] |
floatArray |
double[] |
jdoubleArray |
java.lang.Throwbale object |
jthrowable |
(图七)
除了基础数据类型的数组、Class、String和Throwable外,其余java对象的数据类型在JNI中都表示为jobject。
JNI中支持很多系统函数操作,这些函数支持上诉的java类型的操作及一些访问对象和注册本地方法,上诉hello-jni的例子中就有FindClass()、RegisterNatives()、NewStringUTF()等,常用的函数大全:http://blog.csdn.net/data_backups/article/details/48030425
还剩一个问题
static JNINativeMethod gMethods[] = {
{"stringFromJNI", "()Ljava/lang/String;", (void*)stringFromJNI_native},
};
是什么?
这个是java对应JNI函数的一些签名信息,为了表明java的方法和JNI函数的一个一对一的关系,格式:
{“Java方法名”, “(参数1类型标示参数2类型标示..参数n类型标示)返回值类型标示”, JNI函数名 }
签名类型标示表:
Java Type |
Paramter Type |
boolean |
Z |
byte |
B |
char |
C |
short |
S |
int |
I |
long |
J |
float |
F |
double |
D |
String |
L/java/langaugeString; |
int[] |
[I |
double[] |
jdoubleArray |
类型标示写错了就会注册失败,所以最好不要手写,用工具生成最靠谱。
Java提供了一个签名工具javap,使用方法:
Javap -s -p xxx.class
在hello-jni这个例子里就是如下用法
(图八)
把解析出来的函数签名对应的拷贝道签名数组的位置即可。
基本流程弄清楚了,就可以搞一些复杂的。
依次介绍如下内容:
1)传递基础数据类型
2)传递返回字符串类型
3)传递返回数组
4)Native层操作java类属性
5)Native层回调java方法
6)传递返回一个java类
7)Native线程回调java方法
8)Native调用第三方库
代码下载:http://download.csdn.net/detail/data_backups/9070847
C语言和C++对应的JNI函数稍有些不同
例如,对于生成一个jstring类型的方法转换分别如下:
C编程环境中使用方法为:(*env) ->NewStringUTF(env , "123") ;
C++编程环境中则是: env ->NewStringUTF( "123") ; (使用起来更简单)
Java
//Primitive Types
int ret;
boolean a = true;
byte b = 1;
char c = 2;
short d = 3;
int e = 4;
long f = 5;
float h = 6.1f;
double j = 6.2;
ret = PrimitiveTypes(a, b, c, d, e, f, h, j);
Log.d(TAG, "app PrimitiveTypes return:" + ret);
private native int PrimitiveTypes(boolean a, byte b, char c, short d, int e, long f, float h, double i);//声明
JNI
jint
PrimitiveTypes_native(JNIEnv *env, jobject thiz, jboolean a, jbyte b, jchar c, jshort d, jint e, jlong f, jfloat h, jdouble i)
{
long _f;
float _h;
double _i;
_f = f;
_h = h;
_i = i;
LOGD("JNI: call PrimitiveTypes_native");
LOGD("JNI: jboolean a=%d", a);
LOGD("JNI: jbyte b=%d", b);
LOGD("JNI: jchar c=%d", c);
LOGD("JNI: jshort d=%d", d);
LOGD("JNI: jint d=%d", e);
LOGD("JNI: jlong d=%ld", _f);
LOGD("JNI: jfloat h=%f", _h);
LOGD("JNI: jfloat h=%f", _i);
return 0;
}
//签名
{"PrimitiveTypes", "(ZBCSIJFD)I", (void*)PrimitiveTypes_native},
jstring stringFromJNI_native( JNIEnv* env, jobject thiz ) 就属于返回字符串的例子。
JNI 层的jstring对象可以看成java层的String,但java String存储的是Unicode格式的字符串,而JNI层定义的字符串为UTF-8格式的。调用JNIEnv的NewStringUTF可将根据native的一个UTF-8字符串得到一个jstring对象,这个函数用的最多。
JNI还提供了GetStringChars函数,可将java String对象转换成本地字符串。
如果调用了上诉JNI函数,在做完工作之后需要调用ReleaseStringUTFChars函数来释放资源(如果是Unicode的就调用ReleaseStringChars),否则会导致JVM内存泄露。
//Reference Types Array
byte array1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
byte array2[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
ByteBuffer mDirectBuffer = ByteBuffer.allocateDirect(10);
ReferenceTypesSetByteArray1(array1);
Log.d(TAG, "app array1:");
printBuffer(array1);
ReferenceTypesSetByteArray2(array2);
Log.d(TAG, "app array2:");
printBuffer(array2);
ReferenceTypesSetDirectIntArray(mDirectBuffer, 10);
Log.d(TAG, "app DirectBuffer:");
printBuffer(mDirectBuffer.array());
byte[] buffer = ReferenceTypesGetByteArray();
Log.d(TAG, "app GetArray:");
printBuffer(buffer);
private native void ReferenceTypesSetByteArray1(byte[] array);
private native void ReferenceTypesSetByteArray2(byte[] array);
private native void ReferenceTypesSetDirectIntArray(Object buffer, int len);
private native byte[] ReferenceTypesGetByteArray();
private void printBuffer( byte[] buffer )
{
StringBuffer sBuffer = new StringBuffer();
for( int i=0; i
#define TEST_BUFFER_SIZE 10
void
ReferenceTypesSetByteArray1_native(JNIEnv *env, jobject thiz, jbyteArray buffer)
{
int len;
char array[TEST_BUFFER_SIZE];
len = (*env)->GetArrayLength(env, buffer);
//直接将Java端的数组拷贝到本地的数据中,建议使用这种方式,更加安全
(*env)->GetByteArrayRegion(env, buffer, 0, len, array);
//可以通过array来访问这段数组的值了,注意,修改的只是本地的值,Java端不会同时改变数组的值
int i=0;
for( i=0; iGetArrayLength(env, buffer);
//将本地指针指向含有Java端数组的内存地址,依赖Jvm的具体实现,可能是锁住Java端的那段数组不被回收(增加引用计数),
//也可能所Jvm在堆上对该数组的一份拷贝
//速度和效率比GetByteArrayRegion方法要高很多
char * pBuffer = (*env)->GetByteArrayElements(env,buffer,NULL);
if( pBuffer == NULL ) {
LOGE("GetIntArrayElements Failed!");
return;
}
for (i=0; i < len; i++) {
array[i] = pBuffer[i];
LOGD("JNI: pBuffer[%d]=%d", i, pBuffer[i]);
}
//可以通过pBuffer指针来访问这段数组的值了,注意,修改的是堆上的值,Java端可能会同步改变,依赖于Jvm的具体实现,不建议通过本方法改变Java端的数组值
for( i=0; iReleaseByteArrayElements(env,buffer,pBuffer,0);
}
void
ReferenceTypesSetDirectIntArray_native(JNIEnv *env, jobject thiz, jbyteArray buffer, int len)
{
//无需拷贝,直接获取与Java端共享的直接内存地址(效率比较高,但object的构造析构开销大,建议长期使用的大型buffer采用这种方式)
unsigned char * pBuffer = (unsigned char *)(*env)->GetDirectBufferAddress(env,buffer);
if( pBuffer == NULL ) {
LOGE("GetDirectBufferAddress Failed!");
return;
}
//可以通过pBuffer指针来访问这段数组的值了,注意,修改数组的值后,Java端同时变化
int i=0;
for( i=0; iNewByteArray(env,TEST_BUFFER_SIZE);
//将传递数据拷贝到java端
(*env)->SetByteArrayRegion(env, array, 0, TEST_BUFFER_SIZE, buffer);
return array;
}
//签名
{"ReferenceTypesSetByteArray1", "([B)V", ReferenceTypesSetByteArray1_native},
{"ReferenceTypesSetByteArray2", "([B)V", ReferenceTypesSetByteArray2_native},
{"ReferenceTypesSetDirectIntArray", "(Ljava/lang/Object;I)V", ReferenceTypesSetDirectIntArray_native},
{"ReferenceTypesGetByteArray", "()[B", ReferenceTypesGetByteArray_native},
private String name = "Java layer";//定义类属性
Log.d(TAG, "app print original name: " + name);
OperateJavaAttribute();
Log.d(TAG, "app print new name: " + name);
private native void OperateJavaAttribute();//声明
void
OperateJavaAttribute_native(JNIEnv *env, jobject thiz)
{
//获得jfieldID 以及 该字段的初始值
jfieldID nameFieldId ;
jclass cls = (*env)->GetObjectClass(env, thiz); //获得Java层该对象实例的类引用,即HelloJNI类引用
nameFieldId = (*env)->GetFieldID(env, cls , "name" , "Ljava/lang/String;"); //获得属性句柄
if(nameFieldId == NULL)
{
LOGE("can not find java field id");
}
jstring javaNameStr = (jstring)(*env)->GetObjectField(env, thiz ,nameFieldId); // 获得该属性的值
const char * c_javaName = (*env)->GetStringUTFChars(env, javaNameStr , NULL); //转换为 char *类型
LOGD("JNI: java name is: %s", c_javaName);
(*env)->ReleaseStringUTFChars(env, javaNameStr , c_javaName); //释放局部引用
//构造一个jString对象
char * c_ptr_name = "Native layer";
jstring cName = (*env)->NewStringUTF(env, c_ptr_name); //构造一个jstring对象
(*env)->SetObjectField(env, thiz , nameFieldId , cName); // 设置该字段的值
}
//签名
{"OperateJavaAttribute", "()V", OperateJavaAttribute_native},
public void Java_method(String fromNative) //Native层会调用Java_method()方法
{
Log.d(TAG, "app java function invoked by native function: " + fromNative);
}
NativeCallbackJava();//Native层会调用Java_method()方法
//声明
private native void NativeCallbackJava();
void
NativeCallbackJava_native(JNIEnv *env, jobject thiz)
{
//回调Java中的方法
jclass cls = (*env)->GetObjectClass(env, thiz);//获得Java类实例
jmethodID callbackID = (*env)->GetMethodID(env, cls, "Java_method" , "(Ljava/lang/String;)V") ;//获得该回调方法句柄
if(callbackID == NULL)
{
LOGE("getMethodId is failed \n");
}
jstring native_desc = (*env)->NewStringUTF(env, "callback java I am form Native");
(*env)->CallVoidMethod(env, thiz , callbackID , native_desc); //回调该方法,并且传递参数值
}
//签名
{"NativeCallbackJava", "()V", NativeCallbackJava_native},
package com.example.hellojni;
public class Employee {
private String name;
private int age;
//构造函数,什么都不做
public Employee(){ }
public Employee(int age ,String name){
this.age = age ;
this.name = name ;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name){
this.name = name;
}
}
//native operate java class
Employee someone = NativeGetEmployeeInfo();//native层创建并返回java类
Log.d(TAG, "app someone name:" + someone.getName());
Log.d(TAG, "app someone age:" + someone.getAge());
TransferEmployeeInfo(someone);//java向native层传递java类,并在native层打印出来。
//声明
private native Employee NativeGetEmployeeInfo();
private native void TransferEmployeeInfo(Employee someone);
jobject
NativeGetEmployeeInfo_native(JNIEnv *env, jobject thiz)
{
LOGD("JNI invoke NativeGetEmployeeInfo_native");
jclass employeecls = (*env)->FindClass(env, "com/example/hellojni/Employee");
//获得得该类型的构造函数 函数名为 返回类型必须为 void 即 V
//注意:该 ID特指该类class的构造函数ID , 必须通过调用 GetMethodID() 获得,且调用时的方法名必须为 ,而返回类型必须为 void (V)
jmethodID constrocMID = (*env)->GetMethodID(env, employeecls, "", "(ILjava/lang/String;)V");
jstring str = (*env)->NewStringUTF(env, "xiaoming");
jobject employeeobj = (*env)->NewObject(env, employeecls,constrocMID, 30, str); //构造一个对象,调用该类的构造函数,并且传递参数
return employeeobj;
}
void
TransferEmployeeInfo_native(JNIEnv *env, jobject thiz, jobject employeeobj)
{
LOGD("JNI invoke TransferEmployeeInfo_native");
jclass employeecls = (*env)->GetObjectClass(env, employeeobj); //或得Student类引用
if(employeecls == NULL)
{
LOGE("JNI GetObjectClass failed");
}
jfieldID ageFieldID = (*env)->GetFieldID(env, employeecls,"age","I"); //获得得Student类的属性id
jfieldID nameFieldID = (*env)->GetFieldID(env, employeecls,"name","Ljava/lang/String;"); // 获得属性ID
jint age = (*env)->GetIntField(env, employeeobj , ageFieldID); //获得属性值
jstring name = (jstring)(*env)->GetObjectField(env, employeeobj , nameFieldID);//获得属性值
const char * c_name = (*env)->GetStringUTFChars(env, name ,NULL);//转换成 char *
LOGD("JNI name:%s, age:%d", c_name, age);
(*env)->ReleaseStringUTFChars(env, name,c_name); //释放引用
//LOGD("JNI name:%s, age:%d", c_name, age); 不能再释放后在调用
}
//签名
{"NativeGetEmployeeInfo", "()Lcom/example/hellojni/Employee;", NativeGetEmployeeInfo_native},
{"TransferEmployeeInfo", "(Lcom/example/hellojni/Employee;)V",TransferEmployeeInfo_native},
public void Java_method(String fromNative) //Native层会调用Java_method()方法
{
Log.d(TAG, "app java function invoked by native function: " + fromNative);
}
//native pthread callback java
NativePthreadCallbackJava();//native层创建的线程去回调java
//声明
private native void NativePthreadCallbackJava();
JavaVM * gJavaVM;
jobject gJavaObj;
static void* native_thread_exec(void *arg) {
JNIEnv *env;
LOGD("JNI: pthread tid=%lu, pid=%lu", (unsigned long)gettid(), (unsigned long)getpid());
//从全局的JavaVM中获取到环境变量
(*gJavaVM)->AttachCurrentThread(gJavaVM,&env, NULL);
// //获取Java层对应的类
jclass cls = (*env)->GetObjectClass(env,gJavaObj);
if( cls == NULL ) {
LOGE("JNI Fail to find javaClass");
return 0;
}
// //获取Java层被回调的函数
jmethodID callbackID = (*env)->GetMethodID(env, cls, "Java_method" , "(Ljava/lang/String;)V") ;//获得该回调方法句柄
if(callbackID == NULL)
{
LOGE("getMethodId is failed \n");
}
jstring str = (*env)->NewStringUTF(env, "native pthread callback java I am form native"); //构造一个jstring对象
(*env)->CallVoidMethod(env, gJavaObj , callbackID , str); //回调该方法,并且传递参数值
(*gJavaVM)->DetachCurrentThread(gJavaVM);
}
void
NativePthreadCallbackJava_native(JNIEnv *env, jobject thiz)
{
LOGD("JNI: master tid=%lu, pid=%lu", (unsigned long)gettid(), (unsigned long)getpid());
//注意,直接通过定义全局的JNIEnv和jobject变量,在此保存env和thiz的值是不可以在线程中使用的
//线程不允许共用env环境变量,但是JavaVM指针是整个jvm共用的,所以可以通过下面的方法保存JavaVM指针,在线程中使用
(*env)->GetJavaVM(env,&gJavaVM);
//同理,jobject变量也不允许在线程中共用,因此需要创建全局的jobject对象在线程中访问该对象
gJavaObj = (*env)->NewGlobalRef(env,thiz);
//通过pthread库创建线程
pthread_t threadId;
if( pthread_create(&threadId,NULL,native_thread_exec,NULL) != 0 ) {
LOGE("native_thread_start pthread_create fail !");
return;
}
LOGD("Create Pthread success");
pthread_join(threadId, NULL);
//在native代码不再需要访问一个全局引用的时候,应该调用DeleteGlobalRef来释放它。
(*env)->DeleteGlobalRef(env, gJavaObj);
gJavaObj = NULL;
}
//签名
{"NativePthreadCallbackJava", "()V", NativePthreadCallbackJava_native},
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libsum
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_SRC_FILES := sum.c
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false
LOCAL_SHARED_LIBRARIES := \
libc \
libcutils \
libutils \
liblog
include $(BUILD_SHARED_LIBRARY)
int sum_Library(int a, int b)
{
return a + b;
}
include/sum.h
#ifndef _SUM__
#define _SUM__
int sum_Library(int a, int b);
#endif// _SUM__
LOCAL_PATH := $(call my-dir)
#add prebuild library
#$(warning ****LOCAL_PATH**** )
#$(warning $(LOCAL_PATH))
include $(CLEAR_VARS)
LOCAL_MODULE := libsum
LOCAL_SRC_FILES := prebuilt/libsum.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/prebuilt/include
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
LOCAL_SHARED_LIBRARIES := libsum
LOCAL_LDLIBS := -llog -lz -lm
include $(BUILD_SHARED_LIBRARY)
//native call Library
int sum = NativeCallLibrary_sum(2, 3);
Log.d(TAG, "app 2+3=" + sum);
//声明
private native int NativeCallLibrary_sum(int a, int b);
//加载动态库
System.loadLibrary("sum");
JNI
jint
NativeCallLibrary_sum_native(JNIEnv *env, jobject thiz, jint a, jint b)
{
return sum_Library(a, b);
}
//签名
{"NativeCallLibrary_sum", "(II)I", NativeCallLibrary_sum_native},