打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
JNI与Android VM之间的关系---- 本文摘录自 高焕堂老师 的Android课...
首页 资讯 空间 学生 论坛 博客 下载 读书 网摘 程序员 外包 书店 网址 CTO俱乐部 乐知教育 D9区
水龙吟的备忘录
条新通知 登录 注册 欢迎 退出 我的博客 配置 写文章 文章管理 博客首页   全站 当前博客  空间 博客 好友 相册 留言 用户操作
[留言]  [发消息]  [加为好友] 
订阅我的博客
    
 
 
 
[编辑]evanwu_85的公告
[编辑]文章分类
Agritourism&protocol
Android
Hardware Module
Linux
路在何方
存档
2010年07月(1)
2010年06月(3)
2010年05月(3)
2010年04月(1)
2010年03月(2)
2009年12月(3)
公告: [意见反馈][官方博客]   JNI与Android VM之间的关系---- 本文摘录自 高焕堂老师 的Android课程讲议 收藏
1. 如何载入*.so文件
由于Android的应用层级类别都是以Java撰写的,这些Java类别转译为Dex型式的Bytecode之后,必须仰赖Dalvik虚拟机(VM: Virtual Machine)来执行。
另外,当java需要调用c native组件时,VM就会去加载本地的c组件,让java函数能顺利的调用到C函数。此时,VM扮演着桥梁的角色,让java和c组件能通过透明的JNI接口相互沟通。
应用层级的Java类是在虚拟机(VM: Vitual Machine)上执行的,而C组件不是在VM上执行,那么Java程序又如何要求VM去加载(Load)所指定的C组件呢? 可使用下述指令:
System.loadLibrary(*.so的文件名);
如:
public class MediaPlayer{   
    static {
System.loadLibrary("media_jni");
    }
……..
}
这要求VM去加载Android的/system/lib/libmedia_jni.so文件。载入*.so档之后,Java类别与*.so档就汇合起来,一起执行了。
2. 如何撰写*.so的入口函数
---- JNI_OnLoad()与JNI_OnUnload()函数之用途
当VM执行到System.loadLibrary()函数时,首先会去执行C组件里的JNI_OnLoad()函数。它的用途有二:
1. 告诉VM此C组件使用那一个JNI版本。如果你的*.so文件没有提供JNI_OnLoad()函数,VM会默认该*.so檔是使用最老的JNI 1.1版本。由于新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 1.4的 java.nio.ByteBuffer, 就必须藉由JNI_OnLoad()函数来告知VM。
2. 由于VM执行到System.loadLibrary()函数时,就会立即先呼叫JNI_OnLoad(),所以C组件的开发者可以藉由JNI_OnLoad()来进行C组件内的初期值之设定(Initialization)。
例如,在Android的/system/lib/libmedia_jni.so档案里,就提供了JNI_OnLoad()函数,其代码段为:
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaPlayer-JNI"
………
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed\n");
        goto bail;
    }
    assert(env != NULL);
    if (register_android_media_MediaPlayer(env) < 0) {
        LOGE("ERROR: MediaPlayer native registration failed\n");
        goto bail;
    }
    if (register_android_media_MediaRecorder(env) < 0) {
        LOGE("ERROR: MediaRecorder native registration failed\n");
        goto bail;
    }
    if (register_android_media_MediaScanner(env) < 0) {
        LOGE("ERROR: MediaScanner native registration failed\n");
        goto bail;
    }
    if (register_android_media_MediaMetadataRetriever(env) < 0) {
        LOGE("ERROR: MediaMetadataRetriever native registration failed\n");
        goto bail;
    }
    /* success -- return valid version number */
    result = JNI_VERSION_1_4;
bail:
    return result;
}
// KTHXBYE
此函数回传JNI_VERSION_1_4值给VM,于是VM知道了其所使用的JNI版本了。此外,它也做了一些初期的动作(可呼叫任何本地函数),例如指令:
   if (register_android_media_MediaPlayer(env) < 0) {
        LOGE("ERROR: MediaPlayer native registration failed\n");
        goto bail;
    }
就将此组件提供的各个本地函数(Native Function)登记到VM里,以便能加快后续呼叫本地函数的效率。
JNI_OnUnload()函数与JNI_OnLoad()相对应的。在加载C组件时会立即呼叫JNI_OnLoad()来进行组件内的初期动作;而当VM释放该C组件时,则会呼叫JNI_OnUnload()函数来进行善后清除动作。当VM呼叫JNI_OnLoad()或JNI_Unload()函数时,都会将VM的指标(Pointer)传递给它们,其参数如下:
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
………
}
jint JNI_OnUnload(JavaVM* vm, void* reserved){
………
}
在JNI_OnLoad()函数里,就透过VM之指标而取得JNIEnv之指标值,并存入env指针变量里,如下述指令:
jint JNI_OnLoad(JavaVM* vm, void* reserved){
     JNIEnv* env = NULL;
     jint result = -1;
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
      LOGE("ERROR: GetEnv failed\n");
  goto bail;
    }
}
由于VM通常是多线程(Multi-threading)的执行环境。每一个线程在呼叫JNI_OnLoad()时,所传递进来的JNIEnv指标值都是不同的。为了配合这种多线程的环境,C组件开发者在撰写本地函数时,可藉由JNIEnv指标值之不同而避免线程的数据冲突问题,才能确保所写的本地函数能安全地在Android的多线程VM 里安全地执行。基于这个理由,当在呼叫C组件的函数时,都会将JNIEnv指标值传递给它,如下:
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
……….
    if (register_android_media_MediaPlayer(env) < 0) {
……….
   }
}
这JNI_OnLoad()呼叫register_android_media_MediaPlayer(env)函数时,就将env指标值传递过去。如此,在register_android_media_MediaPlayer()函数就能藉由该指标值而区别不同的线程,以便化解数据冲突的问题。
例如,在register_android_media_MediaPlayer()函数里,可撰写下述指令:
if ((*env)->MonitorEnter(env, obj) != JNI_OK) {
……….
}
查看是否已经有其他线程进入此对象,如果没有,此线程就进入该对象里执行了。还有,也可撰写下述指令:
if ((*env)->MonitorExit(env, obj) != JNI_OK) {
………
}
查看是否此线程正在此对象内执行,如果是,此线程就会立即离开。
3. registerNativeMethods()函数之用途
应用层级的Java类别透过VM而呼叫到本地函数。一般是依赖VM去寻找*.so里的本地函数。如果需要连续呼叫很多次,每次都需要寻找一遍,会多花许多时间。此时,组件开发者可以自行将本地函数向VM进行登记。例如,在Android的/system/lib/libmedia_jni.so档案里的代码段如下:
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaPlayer-JNI"
………
static JNINativeMethod gMethods[] = {
    {"setDataSource",   "(Ljava/lang/String;)V",
  (void *)android_media_MediaPlayer_setDataSource},
    {"setDataSource",    "(Ljava/io/FileDescriptor;JJ)V",
(void *)android_media_MediaPlayer_setDataSourceFD},
    {"prepare",   "()V",     (void *)android_media_MediaPlayer_prepare},
    {"prepareAsync",   "()V",   (void *)android_media_MediaPlayer_prepareAsync},
    {"_start",   "()V",   (void *)android_media_MediaPlayer_start},
    {"_stop",   "()V",    (void *)android_media_MediaPlayer_stop},
    {"getVideoWidth",   "()I",      (void *)android_media_MediaPlayer_getVideoWidth},
    {"getVideoHeight",   "()I",      (void *)android_media_MediaPlayer_getVideoHeight},
    {"seekTo",              "(I)V",      (void *)android_media_MediaPlayer_seekTo},
    {"_pause",              "()V",       (void *)android_media_MediaPlayer_pause},
    {"isPlaying",           "()Z",       (void *)android_media_MediaPlayer_isPlaying},
    {"getCurrentPosition",  "()I",  (void *)android_media_MediaPlayer_getCurrentPosition},
    {"getDuration",         "()I",     (void *)android_media_MediaPlayer_getDuration},
    {"_release",            "()V",      (void *)android_media_MediaPlayer_release},
    {"_reset",              "()V",        (void *)android_media_MediaPlayer_reset},
    {"setAudioStreamType",  "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
    {"setLooping",      "(Z)V",      (void *)android_media_MediaPlayer_setLooping},
    {"setVolume",      "(FF)V",     (void *)android_media_MediaPlayer_setVolume},
    {"getFrameAt",     "(I)Landroid/graphics/Bitmap;",
  (void *)android_media_MediaPlayer_getFrameAt},
    {"native_setup",    "(Ljava/lang/Object;)V",
           (void *)android_media_MediaPlayer_native_setup},
    {"native_finalize",     "()V",    (void *)android_media_MediaPlayer_native_finalize},
};
………
static int register_android_media_MediaPlayer(JNIEnv *env){
………
    return AndroidRuntime::registerNativeMethods(env,
                "android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
……….
//
jint JNI_OnLoad(JavaVM* vm, void* reserved){
………
    if (register_android_media_MediaPlayer(env) < 0) {
        LOGE("ERROR: MediaPlayer native registration failed\n");
        goto bail;
    }
……….
}
当VM载入libmedia_jni.so档案时,就呼叫JNI_OnLoad()函数。接着,JNI_OnLoad()呼叫register_android_media_MediaPlayer()函数。此时,就呼叫到AndroidRuntime::registerNativeMethods()函数,向VM(即AndroidRuntime)登记gMethods[]表格所含的本地函数了。简而言之,registerNativeMethods()函数的用途有二:
1. 更有效率去找到函数。
2. 可在执行期间进行抽换。由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时,可多次呼叫registerNativeMethods()函数来更换本地函数之指针,而达到弹性抽换本地函数之目的。
发表于 @ 2010年06月26日 22:10:00 | 评论( loading... ) | 编辑| 举报| 收藏
旧一篇:多媒体算法入门之图形图像(BMP) | 新一篇:目录(待续)
查看最新精华文章 请访问博客首页相关文章 发表评论 表 情: 
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/evanwu_85/archive/2010/06/26/5696616.aspx
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
JNI技术与Android应用(0)
认识*.so里的JNI_OnLoad()函数
Android JNI知识简介
【转】JNI 之二 :java & c/c++ 相互通信及调用
Android中JNI编程的那些事儿 - ------------------------...
Android JNI(实现自己的JNI_OnLoad函数)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服