深入理解JNI技术

一、JNI是什么?

JNI是Java Native Interface的缩写,译为Java本地调用。JNI是一种技术。

二、JNI技术的用途?

  • Java程序中的函数调用Native程序中的函数。Native一般指使用C/C++编写的函数。

  • Native程序中的函数调用Java程序中的函数。

三、注册JNI函数

  • 静态注册

  Java层函数通过Java编译成.class文件,再通过Javah工具将将.class生成为JNI层的*.h头文件,在*.h头文件里有对应Java层的函数,在JNI层实现相关函数即可。

javah -o test packagename.classname

  • 动态注册

  Java Native函数与JNI函数是一一对应的关系,所以,有一个数据结构存储着对应关系,这个数据结构就是JNINativeMethod结构体。

typedef struct {

constchar* name;

constchar* signature;

void* fnPtr;

} JNINativeMethod;

JNINativeMethod name:Java函数名称,不包括包路径。

JNINativeMethod signature:Java函数签名,用字符串存储,签名信息由参数类型+返回值类型组成。

JNINativeMethod fnPtr:JNI层对应的函数指针,注意fnPtr是Viod*。

  在运行时,AndroidRuntime类提供方法registerNativeMethod函数完成java函数的注册。registerNativeMethod函数通过调用jniRegisterNativeMethods函数实现函数注册。jniRegisterNativeMethods函数是Android平台提供的帮助函数。

  那么,在什么时候完成注册或者函数调用呢?

  在Java层通过System.loadLibrary函数加载JNI动态库,在System.loadLibrary函数调用完成后,会调用JNI_OnLoad函数,如果有就调用它,函数注册或者相关初始化在JNI_OnLoad函数中完成。

/**

* Loads the native library specified by the <code>libname</code>

* argument. The <code>libname</code> argument must not contain any platform

* specific prefix, file extension or path. If a native library

* called <code>libname</code> is statically linked with the VM, then the

* JNI_OnLoad_<code>libname</code> function exported by the library is invoked.

* See the JNI Specification for more details.

*

* Otherwise, the libname argument is loaded from a system library

* location and mapped to a native library image in an implementation-

* dependent manner.

* <p>

* The call <code>System.loadLibrary(name)</code> is effectively

* equivalent to the call

* <blockquote><pre>

* Runtime.getRuntime().loadLibrary(name)

* </pre></blockquote>

*

* @param libname the name of the library.

* @exception SecurityException if a security manager exists and its

* <code>checkLink</code> method doesn't allow

* loading of the specified dynamic library

* @exception UnsatisfiedLinkError if either the libname argument

* contains a file path, the native library is not statically

* linked with the VM, or the library cannot be mapped to a

* native library image by the host system.

* @exception NullPointerException if <code>libname</code> is

* <code>null</code>

* @see java.lang.Runtime#loadLibrary(java.lang.String)

* @see java.lang.SecurityManager#checkLink(java.lang.String)

*/

@CallerSensitive

publicstaticvoid loadLibrary(String libname) {

Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);

}

  JNI_OnLoad是JNI层方法

jint JNI_OnLoad(JavaVM* vm, void* reserved __unused)

{

// 函数注册

}

四、数据类型转换

  Java层数据类型在JNI层有一一对应的数据类型。  

五、垃圾回收

  在Java中,创建对象使用完后,通过Java GC回收对象和释放内存。那么,Java GC对JNI层有什么影响?Java层变量传入JNI层后如何使用?

  如下代码通过Android源码修改:

// Adnroid源码 frameworks/base/core/jni/android_media_MediaRecorder.cpp

JNIMediaRecorderListener::JNIMediaRecorderListener(JNIEnv* env, jobject thiz, jobject weak_thiz)

{

// Hold onto the MediaRecorder class for use in calling the static method

// that posts events to the application thread.

jclass clazz = env->GetObjectClass(thiz);

if (clazz == NULL) {

ALOGE("Can't find android/media/MediaRecorder");

jniThrowException(env, "java/lang/Exception", NULL);

return;

}

mClass = (jclass)env->NewGlobalRef(clazz);

// We use a weak reference so the MediaRecorder object can be garbage collected.

// The reference is only used as a proxy for callbacks.

mObject = weak_thiz;

}

  上面代码正常情况是没问题的,如果此广告在Java层调用将传递参数到JNI层,通过mOjbect = week_thiz赋值,当前Java层GC回收了week_thiz变量。那么,在其它地方使用mObject变量就会出现异常,mObject指向一个野指针。

  那么,有一个疑问,正常在Java层mOjbect = week_thiz这个赋值,引用计数加1,不应该会被GC回收的。但是,需要注意在JNI层mOjbect = week_thiz这样的语句,是不会增加引用计数的。

  正确写法,未修改Android源码,上面的源码是人为改错的:

JNIMediaRecorderListener::JNIMediaRecorderListener(JNIEnv* env, jobject thiz, jobject weak_thiz)

{

// Hold onto the MediaRecorder class for use in calling the static method

// that posts events to the application thread.

jclass clazz = env->GetObjectClass(thiz);

if (clazz == NULL) {

ALOGE("Can't find android/media/MediaRecorder");

jniThrowException(env, "java/lang/Exception", NULL);

return;

}

mClass = (jclass)env->NewGlobalRef(clazz);

// We use a weak reference so the MediaRecorder object can be garbage collected.

// The reference is only used as a proxy for callbacks.

mObject = env->NewGlobalRef(weak_thiz);

}

  在JNI层引用分为Local Reference(本地引用)和Global Reference(全局引用),还有一种特殊的引用 Weak Global Reference(弱全局引用)。

  Local Reference:本地引用,在JNI层函数中使用的非全局引用都是Local Reference,包括函数调用传递的参数和JNI层本地创建的jobject。Local Reference在JNI最大的好处就是,在JNI层函数返回后,这些jobject就可能被GC回收。

  Global Reference:全局引用,全局引用的对象不主动释放,那么,将永远不会被GC回收。

  Weak Global Reference:弱全局引用,一种特殊的全局引用,在程序运行过程中可能被GC回收。  

六、异常处理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值