JVM加载完本地库会调用JNI_OnLoad()函数

当Android的 VM(VirtualMachine)执行到C组件(即*so文件)里的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;

}

就将此组件提供的各个本地函数(NativeFunction)登记到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) {

………

}

查看是否此执行绪正在此对象内执行,如果是,此执行绪就会立即离开。

Android JNI_OnLoad()函数相关推荐

  1. Android JNI(实现自己的JNI_OnLoad函数)

    实现JNI中本地函数注册可以两种方式: (1)采用默认的本地函数注册流程. (2)自己重写JNI_OnLoad()函数.(本文介绍)(Android中采用这种) Java端代码: package co ...

  2. android 函数名注册,Android JNI 函数注册的两种方式(静态注册/动态注册)

    在Android开发中,由于种种原因我们需要调用C/C++代码, 这个时候就要用到Android开发者都听说过的JNI(Java Native Interface)了, 在调用JNI相关方法之前, 要 ...

  3. android的动态注册,Android JNI 函数注册的两种方式(静态注册/动态注册)

    JNI/NDK 在Android开发中,由于种种原因我们需要调用C/C++代码, 这个时候就要用到Android开发者都听说过的JNI(Java Native Interface)了, 在调用JNI相 ...

  4. android java调用参数,如何从命令行调用Android JNI函数并传递Java对象参数

    一.前言 当我们对某个使用原生库(native library)的恶意软件或者应用进行分析或渗透测试时,如果能够对库函数进行隔离和执行是再好不过的事情,这样做我们就可以使用其自身的代码来调试对抗恶意软 ...

  5. android 回调函数二:应用实例

    前言:如果对android回调的概念不明白的请看:android 回调函数一:基本概念 1.定义接口 package com.app.util;public interface ZYJCallBack ...

  6. android 回调函数一:基本概念

    1.概念 客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数. 一般说来,C不会自己调用B,C提供B的目的就是让S来调用它,而且是C ...

  7. Android服务函数远程调用源码分析

    在Android服务查询完整过程源码分析中介绍了客户进程向ServiceManager进程查询服务的完整过程,ServiceManager进程根据服务名称在自身维护的服务链表中查找ServiceMan ...

  8. Android回调函数理解

    Android回调函数理解,比如我用一个activity去做显示下载进度的一个进度条,但是下载是另外一个B类来做的,这个时候我Activity获取下载的进度就可以提供一个回调接口,然后让下载类来回调就 ...

  9. Android绘制函数图象及正弦函数的介绍

    零.前言 这篇是为了下一篇做点铺垫,也是来复习一些数学基础 本篇属于休闲娱乐,不要太较真,小科普一下,不喜勿喷 本文知识点前4点你可以随便看看,但第5点非常重要,本文源码见捷文规范 本文知识点: 1) ...

最新文章

  1. mysql导入数据io异常_mysql 数据同步 出现Slave_IO_Running:No问题的解决方法小结
  2. STL priority_queue sort 自定义比较终极模板
  3. 【问题解决方案】ImportError: No module named 'openpyxl'/‘xlrd’
  4. 微信小程序 长按属性
  5. mixamo网站_超全面的素材网站推荐
  6. python相关背景及语言特点
  7. IOS-字符串太长换行拼接
  8. TypeScript中的class声明了什么
  9. 人生历练必备的十个心态(图)
  10. Spring IO platform
  11. groupby索引有效吗_两千字揭密 MySQL 8.0.19 三大索引新功能
  12. java direct memory_第一讲  JVM内存四大类型:Heap,Stack,Contant,DirectMemory等
  13. 37. Element appendChild() 方法
  14. 商城项目面试问题整理
  15. 《现代操作系统(中文第四版)》课后习题答案 第三章 内存管理
  16. python手机充值代码_不用框架,原生使用python做注册接口/登陆接口/充值接口的测试,做的数据/代码分离...
  17. 信用卡号码的解析(转)
  18. 理财入门《小狗钱钱》阅读总结
  19. 如何准确理解Amdahl定律,并讨论计算机系统加速比
  20. 爬取集思录可转债成交额

热门文章

  1. 让Lua支持Linq吧
  2. Linux远程复制命令SCP
  3. Atitit ftp原理与解决方案
  4. Vs 控件错位 右侧资源管理器文件夹点击也不管用,显示异常
  5. 九度oj题目1385:重建二叉树
  6. C# 类的几种方法调用
  7. Lucene排序以及自定义排序
  8. android java json与实体互相转换工具
  9. Ruby: 延迟计算与优化
  10. 20170908校内训练