android --- 深入理解 JNI
JNI : Java Native Inte易做图ce 即JAVA本地调用,为何需要这种技术呢?原因有二:
1、运行JAVA程序的虚拟机是用Native语言编写的,而虚拟机运行在具体的平台上,所以虚拟机本身无法做到平台无关,而利用JNI技术即可对JAVA层屏蔽不同操作系统平台之间的差异,如file,socket等
2、在JAVA语言诞生前,很多程序使用Native语言编写,JAVA直接利用JNI使用,避免造重复轮子的坏名声。而且JNI的运行效率和速度会更高
第一大部分: JAVA如何调用Native函数
通过MediaScanner进行完整解释其工作原理:
调用层次关系,图示:
JAVA层代码分析:MediaScanner.java
public class MediaScanner
{
static { //static 语句,前面的 JAVA学习系列(一) 有说明过这个问题
//加载对应用JNI库,linux上即调用libmedai_jni.so,而windows平台上调用libmedia_jni.dll
System.loadLibrary("media_jni");
native_init(); //调用native函数
}
//非native函数
public void scanDirectories(String[] directories, String volumeName)
//声明native函数,native为JAVA语言的关键字
private static native final void native_init();
private native final void native_setup();
private native final void native_finalize();
private native void processDirectory(String path, String extensions, MediaScannerClient client);
}
总结: 一个是加载jni动态库,二是声明java的native函数
JNI层 android_media_MediaScanner.cpp 分析:
首先来找下java层的native_init函数对应jni层的函数?
首先native_init函数位于android.media包中,它的全路径:android.media.MediaScanner.native_init,由于
在native语言中,"."有特殊的意义,所以用"_"来替换:即上面路径这为:android_media_MediaScanner_native_init
ok,函数名就如此关联起来了。而JNI函数注册有两种方式:
1、静态注册方法
a、编写java代码,然后编译生成.class文件
b、使用javah -o output packagename.classname生成jni层头文件
调用时先加载动态库,然后查找native_init函数的jni函数:android_media_MediaScanner_native_init
如果找到则建立这两个函数的关联关系,即保存jni层函数的函数指针,这个由虚拟机完成。
缺点:javah生成的函数名特别长,不利于书写,且第一次调用时需要根据函数名字搜索建立关联关系。
2、动态注册方法
利用JNINativeMethod结构保存其关系
typedef struct
{
//JAVA中native函数名字
const char *name;
//签名信息,用字符串表示,参数类型及返回值类型的组合
const char *signature;
///JNI层函数函数指针,转换为void*类型
void *fnPtr;
};
static JNINativeMethod gMethods[] = {
{"processDirectory", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
(void *)android_media_MediaScanner_processDirectory},
{"processFile", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
(void *)android_media_MediaScanner_processFile},
{"setLocale", "(Ljava/lang/String;)V", (void *)android_media_MediaScanner_setLocale},
{"extractAlbumArt", "(Ljava/io/FileDescriptor;)[B", (void *)android_media_MediaScanner_extractAlbumArt},
{"native_init", "()V", (void *)android_media_MediaScanner_native_init},
{"native_setup", "()V", (void *)android_media_MediaScanner_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaScanner_native_finalize},
};
这里说明一下签名信息:
因为JAVA支持函数重载,可以定义同名但不同参数的函数,但直接根据函数名是没法找到具体函数的,因此利用参数类型及返回类型
组成签名信息。
常用类型标识符:
类型标识 JAVA类型 字长
Z boolean 8位
B byte 8位
C char 16位 -- 注意哟
S short 16位
I int 32位
J long 64位
F float 32位
D double 64位
L/java/languageString String
[I int[] int数组
[L/java/lang/object Object[] 对象数组
// This function only registers the native methods, and is called from
// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaScanner(JNIEnv *env)
{
//利用registerNativeMethods注册JNI函数
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaScanner", gMethods, NELEM(gMethods));
}
// 加载jni库,查找该库中的JNI_OnLoad函数完成动态注册工作
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
...
if (register_android_media_MediaScanner(env) < 0) {
LOGE("ERROR: MediaScanner native registration failed\n");
goto bail;
}
}
static void
android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jstring extensions, jobject client)
{
MyMediaScannerClient myClient(env, client);
mp->pro
补充:移动开发 , Android ,