连接Java和C/C++层的关键--Android的JNI
来源:互联网 发布:淘宝女装图片素材 编辑:程序博客网 时间:2024/06/05 06:33
JNI的作用
JNI是Java Native Interface(Java本地接口)的缩写。是从Java继承而来的,在Android中JNI的作用大大增强了。Android作为一种嵌入式操作系统,有大量和驱动、硬件相关的功能必须在native层实现,另外一些注重性能、功耗的功能使用C/C++来实现也优于Java来实现。因此,在Android开发中,无论是应用开发,还是系统开发都离不开JNI。
Android中,Java层主要负责UI功能的实现,而C/C++层则完成一些复杂的算法以及和底层交互的功能,因此,Java层和C/C++层交互比较频繁,这就用到了JNI。
Java语音的执行,离不开虚拟机。因此,当需要在Java中调用C/C++层的函数时,需要告诉虚拟机哪个方法代表本地的函数,以及在哪里能找到这个函数,反之类似。但是这两者之间还是有区别的,从Java到C/C++建立的是函数间的关联;而从C/C++到Java,必须先得到Java对象的引用,才能调用该对象的方法。这是因为Java是“纯”面向对象的语言,所以从C/C++到Java,必须和对象打交道,而不想C/C++中对象和函数可以混用。
注意,无论从Java到C/C++,还是从C/C++到Java,两者是在一个线程中运行的。
JNI用法
从Java到C/C++
1、装在JNI动态库
为了使用JNI,在调用本地方法前必须把C/C++代码所在的动态库装载到进程的内存空间中。装载库文件调用的是System类的loadLibrary()方法,如下:
public static void loadLibrary(String libName)
参数是动态库文件名称的一部分。Android JNI动态库的名称必须以“lib”开头,这里传入的参数是去掉前缀“lib”和后缀“.so”的中间部分。由于不同平台动态库的后缀时不一样的,Java希望代码能够跨平台使用,所以参数去掉了和系统相关的部分。
调用loadLibrary()方法不需要指定库文件的路径,Android会在几个系统目录下查找动态库。
为了保证调用native方法前所需要的动态库已经加载,loadLibrary()的调用位置一般放在类的static代码块中,这样进程初始化时就能执行装载语句了。例如,AudioEffect类装载JNI库的代码如下:
public class AudioEffect { static { System.loadLibrary("audioeffect_jni"); native_init(); } ......}
2、定义native方法
在Java类中定义native方法很简单,在方法前加上“native”关键字就可以了,例如:
// --------------------------------------------------------- // Native methods called from the Java side // -------------------- private static native final void native_init();
在native方法中,可以使用任何类型作为参数,包括基本对象类型、数组类型、负责对象等。
3、编写JNI动态库
JNI动态库中定义了一个“JNI_OnLoad()”的函数,这个函数在动态库加载后会被系统调用,用于完成JNI函数的注册。函数原型如下:
jint JNI_OnLoad(JavaVM* vm, void*)
在该函数中最重要的就是调用registerNativeMethods()函数,完成动态库中JNI函数的注册。所谓注册,就是通过一张表把Java类中定义的native方法和本地C函数联系起来,这样Dalvik虚拟机在解析Java类中的native方法时就能查找到对应的C函数。
下面看一个简单的例子:(media\jni\audioeffect\android_media_AudioEffect.cpp)
jint JNI_OnLoad(JavaVM* vm, void* reserved __unused){...... if (register_android_media_AudioEffect(env) < 0) { ALOGE("ERROR: AudioEffect native registration failed\n"); goto bail; }......}
int register_android_media_AudioEffect(JNIEnv *env){ return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));}
// Dalvik VM type signaturesstatic const JNINativeMethod gMethods[] = { {"native_init", "()V", (void *)android_media_AudioEffect_native_init}, {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;Ljava/lang/String;)I", (void *)android_media_AudioEffect_native_setup},...... {"native_setParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_setParameter},......};
static const char* const kClassPathName = "android/media/audiofx/AudioEffect";
registerNativeMtehods()函数的原型是:
int registerNativeMethods(JNIEnv* env, const char* classname, const JNINativeMethod* gMethods, int numMethods);
第二个参数classname是指Java类的权限定类名,但是名称中的“.”要换成“/”。例如上面例子。第三个参数是JNINativeMethod类型的数组,类型定义如下:
typedef struct { const char* name; const char* signature; void* fnPtr;} JNINativeMethod;
类型定义中的成员变量name是指Java类中native方法的名称;signature是native方法的参数签名;fnPtr是指native方法对应的本地函数指针。
本地函数的原型要求如下:
返回类型 函数名(JNIEnv *env, jobject obj, ......);
第一个参数是JNI环境,第二个参数是指调用类的Java对象,后面省略号和具体方法的参数一致。具体例子如下:
static jintandroid_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled){ ......}
4、参数签名
native方法的参数签名使用了一些缩写符号来代表参数类型,签名有参数加返回值组成,参数必须用小括号括起来,没有参数时使用一对空括号。如“(I)V”,表示方法有一个整形参数,无返回值;“([IZ)I”,表示方法有两个参数,第一个参数是整形数组,第二个参数是布尔型,返回类型是整形。
参数签名中数组的表示方法是 在基本类型符号前加上“[”。
复杂类型的参数签名格式是“L”加上“全限定类名”再加上“;”,例如
Ljava/lang/String;
从C/C++到Java
1、生产Java对象
在JNIEnv中生成一个Java对象可以使用函数NewObject(),函数原型如下:
jobject NewObject(jclass clazz, jmethodID methodID, ...)
clazz是指Java类对象(不是类的实例对象),可以通过函数FindClass()得到;参数methodID是指Java类的构造函数。
FindClass()函数原型如下:
jclass FindClass(const char* name);
name是指Java类的名称,如: “/java/lang/String”。jclass用来表示Java类。
调用一个Java对象的方法或者存取一个Java对象的域变量前,要先知道对应的Id。取得方法Id和域变量Id的函数原型如下:
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig);jfieldID GetFieldID(jclass clazz, const char* name, const char* sig);
参数name是指Java类的方法或成员变量名称;sig是指参数类型签名。
如果要获得一个Java类的构造函数,使用函数GetMethodID时,传入的参数name必须是“<init>”才行。
2、调用Java类的方法
env->CallStaticVoidMethod( callbackInfo->audioEffect_class, fields.midPostNativeEvent, callbackInfo->audioEffect_ref, event, arg1, arg2, obj);
3、存取Java类的域变量
env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
JNI环境
待续。。。
ART带来的JNI变化
待续。。。
- 连接Java和C/C++层的关键--Android的JNI
- 连接Java和C/C++层的关键--Android的JNI
- Android JNI实现简单的c层调用Java层函数(C层调用Java层Toast进行提示)
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
- 通过JNI实现c/c++和Android的java层函数互调
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
- (转)cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调 .
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
- Cocos2dx之通过JNI实现c/c++和Android的java层函数互调
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
- cocos2d-x 通过JNI实现c/c++和Android的java层函数交互
- HTML5 <template>标签元素简介
- 断点续传
- 大龄转行IT,记录一下走过路过的坑,希望能对后来人有所帮助
- mybatis入门教程
- 说说css中pt、px、em、rem都扮演了什么角色
- 连接Java和C/C++层的关键--Android的JNI
- 【Scikit-Learn 中文文档】支持向量机
- ASP.NET MVC
- FeUdal Networks for Hierarchical Reinforcement Learning 阅读笔记
- JAVA虚拟机、Dalvik虚拟机和ART虚拟机简要对比
- 【ES系列一】——单机版安装教程
- spring中基于BeanPostProcessor动态替换指定的bean
- linux下find和grep的区别
- centos6上超详细LAMP编译安装过程以及所遇到的问题