通过第一篇文章,大家明白了调用native方法之前,首先要调用System.loadLibrary接口加载一个实现了native方法的动态库才能正常访问,否则就会抛出java.lang.UnsatisfiedLinkError异常,找不到XX方法的提示。现在我们想想,在Java中调用某个native方法时,JVM是通过什么方式,能正确的找到动态库中C/C++实现的那个native函数呢?

JVM查找native方法有两种方式:

1> 按照JNI规范的命名规则

2> 调用JNI提供的RegisterNatives函数,将本地函数注册到JVM中。(后面会详细介绍)

本文通过第一篇文章HelloWorld示例中的Java_com_study_jnilearn_HelloWorld_sayHello函数来详细介绍第一种方式:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_study_jnilearn_HelloWorld */ #ifndef _Included_com_study_jnilearn_HelloWorld
#define _Included_com_study_jnilearn_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_study_jnilearn_HelloWorld
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_study_jnilearn_HelloWorld_sayHello
(JNIEnv *, jclass, jstring); #ifdef __cplusplus
}
#endif
#endif

JNIEXPORT 和 JNICALL的作用:

在上篇文章中,我们在将HelloWorld.c编译成动态库的时候,用-I参数包含了JDK安装目录下的两个头文件目录:

gcc -dynamiclib -o /Users/yangxin/Library/Java/Extensions/libHelloWorld.jnilib jni/HelloWorld.c -framework JavaVM -I/$JAVA_HOME/include -I/$JAVA_HOME/include/darwin

其中第一个目录为jni.h头文件所在目录,第二个是跨平台头文件目录(mac os x系统下的目录名为darwin,在windows下目录名为win32,linux下目录名为linux),用于定义与平台相关的宏,其中用于标识函数用途的两个宏JNIEXPORT
和 JNICALL,就定义在darwin目录下的jni_md.h
头文件中。在Windows中编译dll动态库规定,如果动态库中的函数要被外部调用,需要在函数声明中添加__declspec(dllexport)标识,表示将该函数导出在外部可以调用。在Linux/Unix系统中,这两个宏可以省略不加。这两个平台的区别是由于各自的编译器所产生的可执行文件格式不一样。这里有篇文章详细介绍了两个平台编译的动态库区别:http://www.cnblogs.com/chio/archive/2008/11/13/1333119.html。JNICALL在windows中的值为__stdcall,用于约束函数入栈顺序和堆栈清理的规则。

Windows下jni_md.h头文件内容:

#ifndef _JAVASOFT_JNI_MD_H_
#define _JAVASOFT_JNI_MD_H_ #define JNIEXPORT __declspec(dllexport)
#define JNIIMPORT __declspec(dllimport)
#define JNICALL __stdcall typedef long jint;
typedef __int64 jlong;
typedef signed char jbyte; #endif

Linux下jni_md.h头文件内容:

#ifndef _JAVASOFT_JNI_MD_H_
#define _JAVASOFT_JNI_MD_H_ #define JNIEXPORT
#define JNIIMPORT
#define JNICALL typedef int jint;
#ifdef _LP64 /* 64-bit Solaris */
typedef long jlong;
#else
typedef long long jlong;
#endif typedef signed char jbyte; #endif

从Linux下的jni_md.h头文件可以看出来,JNIEXPORT 和 JNICALL是一个空定义,所以在Linux下JNI函数声明可以省略这两个宏。

函数的命名规则:

用javah工具生成函数原型的头文件,函数命名规则为:Java_类全路径_方法名。如Java_com_study_jnilearn_HelloWorld_sayHello,其中Java_是函数的前缀,com_study_jnilearn_HelloWorld是类名,sayHello是方法名,它们之间用 _(下划线)
连接。

函数参数:

JNIEXPORT jstring JNICALL Java_com_study_jnilearn_HelloWorld_sayHello(JNIEnv *, jclass, jstring);

第一个参数:JNIEnv* 是定义任意native函数的第一个参数(包括调用JNI的RegisterNatives函数注册的函数),用于访问JVM数据结构的JNI函数表指针

第二个参数:调用java中native方法的实例或Class对象,如果这个native方法是实例方法,则该参数是jobject,如果是静态方法,则是jclass

第三个参数:Java对应JNI中的数据类型,Java中String类型对应JNI的jstring类型。(后面会详细介绍JAVA与JNI数据类型的映射关系)

函数返回值类型:

夹在JNIEXPORT和JNICALL宏中间的jstring,表示函数的返回值类型,对应Java的String类型

总结:

当我们熟悉了JNI的native函数命名规则之后,就可以不用通过javah命令去生成相应java native方法的函数原型了,只需要按照函数命名规则编写相应的函数原型和实现即可。

比如com.study.jni.Utils类中还有一个计算加法的native实例方法add,有两个int参数和一个int返回值:public native int add(int num1, int num2),对应JNI的函数原型就是:JNIEXPORT jint JNICALL Java_com_study_jni_Utils_add(JNIEnv *, jobject, jint,jint);

JNI/NDK开发指南(二)——JVM查找java native方法的规则的更多相关文章

  1. JNI/NDK开发指南(开山篇)

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/41759643 相信很多做过Java或Android开发的朋友经常会接触到JNI方面的技术, ...

  2. JNI/NDK开发指南(三)——JNI数据类型及与Java数据类型的映射关系

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/42047899 当我们在调用一个Java native方法的时候.方法中的參数是怎样传递给C ...

  3. JNI/NDK开发指南(一)—— JNI开发流程及HelloWorld

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/41777471 JNI全称是Java Native Interface(Java本地接口)单 ...

  4. JNI/NDK开发指南(十)——JNI局部引用、全局引用和弱全局引用

    转自:http://blog.csdn.net/xyang81/article/details/44657385   这篇文章比较偏理论,详细介绍了在编写本地代码时三种引用的使用场景和注意事项.可能看 ...

  5. JNI/NDK开发指南(九)——JNI调用性能測试及优化

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/44279725 在前面几章我们学习到了.在Java中声明一个native方法,然后生成本地接 ...

  6. JNI/NDK开发指南(四)——字符串处理

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/42066665 从第三章中能够看出JNI中的基本类型和Java中的基本类型都是一一相应的,接 ...

  7. JNI/NDK开发指南(2)

    1.生成动态库.so,存放于手机的system/lib/中(APP怎样将.so存入该文件夹,奇怪?????),Java层调用JNI的类会运行静态代码System.loadLibrary("* ...

  8. Android JNI/NDK开发教程

    JNI/NDK开发指南:http://blog.csdn.net/xyang81/article/details/41759643

  9. JNI/NDK开发

    公司的新需求终于解决完了,离测试和发布还有段时间,第一次体验了下没需求没bug的感觉,真是舒爽~然后翻了翻有什么可以学的.无意翻到了Android后期发展的五大趋势.一.性能优化.二.高级UI.三.J ...

随机推荐

  1. create database xx 或者show database 没有任何反应

    命令是以:结束的,你忘记了,记住,是英文状态下的:

  2. 关于IB_DESIGNABLE / IBInspectable的那些事

    前言 IB_DESIGNABLE / IBInspectable 这两个关键字是在WWDC 2014年”What’s New in Interface Builder”这个Session里面,用Swi ...

  3. Java基础知识强化之IO流笔记43:IO流练习之 复制文本文件的 5 种方式案例

     1. 案例分析: 分析: 复制数据,如果我们知道用记事本打开并能够读懂,就用字符流,否则用字节流. 通过该原理,我们知道我们应该采用字符流更方便一些. 而字符流有5种方式,所以做这个题目我们有5种方 ...

  4. MYSQL小常识

    在mysql里面利用str_to_date()把字符串转换为日期 此处以表T_TGS_ALARMED的BJSJ为例,查询当前时间在此范围之内的数据. insert into T_TGS_ALARMED ...

  5. javascript源码阅读推荐

    作者:马 岩(Furzoom) (http://www.cnblogs.com/furzoom/)版权声明:本文的版权归作者与博客园共同所有.转载时请在明显地方注明本文的详细链接,未经作者同意请不要删 ...

  6. 系统监控、诊断工具之top

    大家对top 命令可能不会陌生,它的作用主要用来监控系统实时负载率.进程的资源占用率及其它各项系统状态属性是否正常. top命令的截图如下: (1)系统.任务统计信息: 前8行是系统整体的统计信息.第 ...

  7. python+django+wusgi+nginx安装部署

    基于centos搭建nginx+uwsgi运行django环境 环境: CentOS 7 nginx/1.9.12 Python 2.7.5 一:安装依赖包5 yum install zlib-dev ...

  8. 【转】Android 应用测试总结

    前提所有的功能分支已完成 启动:1. 启动入口:桌面正常启动,最近运行启动,所有程序列表中启动,锁屏快捷启动2. 其他入口:从其他程序开启应用,从外部以文件形式打开应用(如果有)3. 退回:从其他程序 ...

  9. [转] c# 数据类型占用的字节数

    http://www.cnblogs.com/laozuan/archive/2012/04/24/2467888.html

  10. 解决Android Studio启动速度慢的问题。避免每次启动Android Studio都要fetching Android sdk compoment information。

    Android Studio每次启动都要去fetching sdk,由于Android sdk 官网在大陆连不上,所以每次启动时界面都会停在那里很久. 解决办法就是设置取消每次fetching sdk ...