Java Native Interface,nativeinterface
Java Native Interface,nativeinterface
Java JNI说明文档 : Java Native Interface Specification Contents
1、JNI简介
2、JNI设计概述
3、JNI数据类型
4、JNI函数
5、API环境搭建
结合<深入理解Android>的示例来分析JNI的实现,需要android源码一份,
1、JNI简介
1.1 JNI是什么?
The JNI is a native programming interface. It allows Java code that runs inside a Java Virtual Machine
interoperate with applications and libraries written in other programming languages, such as C, C++,
and assembly。
JNI是一个java程序调用native程序的编程接口,native程序像c,c++等,在Android里面是c/c++。
1.2 为什么要使用JNI ?
java并不能进行所有的编程,此时需要和其他编程语言协作完成任务,例如:
——标准Java类库不支持依赖于平台的功能。例如,linux下一切皆文件,访问文件节点支持最多的是c/c++语言,java类库不支持此类依赖平台的function。
——已经用其它语言编写的库,希望使其通过JNI的Java代码访问。
——某些关键代码放在低级语言中
通过JNI可以做哪些事情?
—— 在java层,可以实现对native函数调用(native层可以认为是 C/C++程序)
—— 在native层,可以创建,检查和更新Java对象(包括数组和字符串),调用Java方法。捕捉和抛出异常等。
2、JNI设计概述
JNI实现架构如下: java层(Java VM) ------- JNI interface ------ Native层
JNI拥有一套机制来实现 java层与native层的相互调用。 例如:(源码均在frameworks\base\media里面)
在frameworks\base\media里面,有java(java代码)和jni(c++代码)两个目录,jni目录编译可生成libmedia_jni.so
库,java可以链接libmedia_jni.so从而实现对c++代码的调用。
Java代码MediaScanner.java
public class MediaScanner
{
static {
System.loadLibrary("media_jni");
native_init();
}
private static native final void native_init();
}
System.loadLibrary("media_jni");//导入JNI层的lib库,在Android下是libmedia_jni.so(不同平台导入的库不一样,在windows下可能是dll库)
native_init();//执行native_init函数
C++代码android_media_MediaScanner.cpp
android_media_MediaScanner_native_init(JNIEnv *env)
{
ALOGV("native_init");
jclass clazz = env->FindClass(kClassMediaScanner);
if (clazz == NULL) {
return;
}
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.context == NULL) {
return;
}
}
这个android_media_MediaScanner_native_init就是上面被调用的native_init函数的实现。
native_init调用过程————动态注册调用
在java层执行System.loadLibrary("media_jni");时,Java VM会到libmedia_jni.so库里面找一个JNI_OnLoad函数执行,jint JNI_OnLoad(JavaVM* vm, void* /* reserved */);
在jni目录下查找这个函数,在android_media_MediaPlayer.cpp里面找到了,它执行一个操作
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
JNIEnv* env = NULL;
register_android_media_MediaScanner(env);
}
在android_media_MediaScanner.cpp中register_android_media_MediaScanner被实现,这个函数调用了
AndroidRuntime::registerNativeMethods(env,
"android/media/MediaScanner", gMethods, NELEM(gMethods));
参数env —— JNIEnv*(保存JNI functions的指针),后面调用android_media_MediaScanner_native_init函数就是通过
它来调用,JNI的API接口也全部通过它来使用。
"android/media/MediaScanner" ————java空间 MediaScanner路径
gMethods ———— 一个java与native匹配的结构体,例如:
{
"native_init",
//java函数名字
"()V", //签名,()里面为空代表没有传递参数,V代表返回值为void。<注:这是为了防止多态问题>
(void *)android_media_MediaScanner_native_init//C++函数的名字
},
NELEM(gMethods) ————Methods的number,gMethod一共有几个
综上: 在java导入lib库之后,找到JNI_OnLoad函数执行,然后执行AndroidRuntime::registerNativeMethods函数。在这个函数中,jni通过系统调用registerNatives函数,把env、Jclass(可以认为是java类在native层的映射)、和gMethods做了关系保存。
上面的方法是Android的动态注册调用,还有通过.h实现的静态注册调用,这里不再多说,原理类似。
从上面的过程可以看出,JNIEnv* env是一个线程相关的(在JNI_OnLoad时被创建),因此多线程操作会产生多个
env指针,这些指针由Java VM来管理。
在Native层,可以通过env指针获得java的对象与方法,问题在于:
java函数{
创建一个对象
调用JNI函数(传递对象引用) ----> 如果在native层,把这个对象赋值给全局变量
返回
}
如果回收对象,则native层的全局变量变为野指针,造成错误。为此,env提供了两个函数接口来实现native层创建
Global and Local references。通过创建Global
变量实现不被回收,但全局变量需要手动回收,否则造成内存泄露。
JNI如何调用java层的方法属性————jmethodID 与 jfieIdID
jmethodID mid = env->GetMethodID(cls, “f”, “(ILjava/lang/String;)V”);通过env函数,cls是Jclass(java类在JNI的映射),f代表函数f,最后为参数。即得到类cls的函数 void f(int,String)的ID.
执行
env->CallDoubleMethod(obj, mid, 10, str);
obj是jobject,代表由Jclass类创造的对象,mid是jmethodID ,后面为函数参数。
jfieIdID的使用与jmethodID 类似,有对应的set与get函数,通过
env->GetFiledID(cls, “P_num”, “J”);到java类cls中找 名字为P_num,类型为long的数据定义。
jmethodID 与 jfieIdID是根据class找到的,与对象(Jobject)无关,因此最好进行存储,防止以后再次使用。存储可以减
少代码编写并增加执行效率。
JNI不会检查程序异常或错误,原因如下:
降低性能,native程序会出现运行时错误!
Reporting
Programming Errors
The JNI does not check for programming errors such as passing in NULL pointers or illegal argument types.
... ...
The programmer must not pass illegal pointers or arguments of the wrong type to JNI functions.
Doing so could result in arbitrary consequences, including a corrupted system state or VM crash。
有两种方法来处理native代码的异常:1. native代码进行检测,出现异常则立刻return
2. 通过调用env提供的一系列函数来处理异常。
ExceptionClear(); ExceptionCheck();
3、 JNI数据类型
——基本类型
除了void之外,其他类型前加j来定义,例如jint,jchar等
#define JNI_FALSE 0
#define JNI_TRUE 1
typedef jint jsize;
——引用类型
jobject jclass;
所有的类型在jni.h文件中都有定义,在 development/ndk/platforms/android-3/include/jni.h
——JNI使用修订的UTF-8来表示字串类型
Modified UTF-8 strings are the same as those used by the Java VM. Modified UTF-8 strings are
encoded so that character sequences that contain only non-null ASCII characters can be represented
using only one byte per character, but all Unicode characters can be represented.
4、JNI函数
JNI函数使用说明,链接 JNI Functions
注意:
Note the use of the term “must” to describe restrictions on JNI programmers. For example, when you see that a certain JNI function must receive
a non-NULL object, it is your responsibility to ensure that NULL is not passed to that JNI function. As a result, a JNI implementation does not need to perform NULL pointer checks in that JNI function.
程序员必须保证一个非NULL的object被传递(如果函数需要一个non-NULL object)
所有的functions都在const struct JNINativeInterface定义(在development/ndk/platforms/android-3/include/jni.h中)
举例:
GetSuperclass
jclass GetSuperclass(JNIEnv *env, jclass clazz);
If clazz
represents any class other than the class Object
, then this function
returns the object that represents the superclass of the class specified by clazz
.
If clazz
specifies the class Object
, or clazz
represents
an interface, this function returns NULL
.
LINKAGE:
Index 10 in the JNIEnv interface function table.
PARAMETERS:
env
: the JNI interface pointer.
clazz
: a Java class object.
RETURNS:
Returns the superclass of the class represented by clazz
, or NULL
.
5、API环境搭建
主要介绍如何搭建JavaVM,并通过JavaVM管理JNIEnv *env线程的操作。这部分在 Android运行环境ART 中实现,在 frameworks\base\core\jni\AndroidRuntime.cpp 下执行。
包括:
Creating
the VM
Attaching to the VM
Detaching from the VM
Unloading the VM
JNI_OnLoad
JNI_OnUnload
JNI_GetDefaultJavaVMInitArgs //Returns JNI_OK
if
the requested version is supported; else returns a JNI error
JNI_GetCreatedJavaVMs //Returns all Java VMs that have been created.
JNI_CreateJavaVM //Loads and initializes a Java VM
DestroyJavaVM //Unloads a Java VM and reclaims its resources
AttachCurrentThread //Attaches the current thread to a Java VM. Returns a JNI interface pointer
AttachCurrentThreadAsDaemon
DetachCurrentThread
GetEnv //get JNIEnv
相关文章
- 暂无相关文章
用户点评