Command.java:
public class Command {
public static native String getStringFromC(String a);
static {
System.loadLibrary("ndk-demo");
}
}
#-d 指明产生文件的目录,
javac ./java/com/popoaichuiniu/jacy/examplelearning/Command.java -d .
#-classpath 指明查找的class文件的路径
#-d 指明产生头文件的目录
javah -classpath . -d ./jni com.popoaichuiniu.jacy.examplelearning.Command
产生如下头文件:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_popoaichuiniu_jacy_examplelearning_Command */
#ifndef _Included_com_popoaichuiniu_jacy_examplelearning_Command
#define _Included_com_popoaichuiniu_jacy_examplelearning_Command
#ifdef __cplusplus
extern "C" {//如果使用C++,包含这个头文件,就需要extern c。
#endif
/*
* Class: com_popoaichuiniu_jacy_examplelearning_Command
* Method: getStringFromC
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_popoaichuiniu_jacy_examplelearning_Command_getStringFromC
(JNIEnv *, jclass, jstring);//注意这里只是声明了函数,并没有实现
#ifdef __cplusplus
}
#endif
#endif
#include<jni.h>
//中包含#include "jni_md.h"
#include "jni_md.h":
(linux)
#ifndef _JAVASOFT_JNI_MD_H_
#define _JAVASOFT_JNI_MD_H_
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility)
#define JNIEXPORT __attribute__((visibility("default")))
#define JNIIMPORT __attribute__((visibility("default")))
#else
#define JNIEXPORT
#define JNIIMPORT
#endif
#define JNICALL
typedef int jint;
#ifdef _LP64 /* 64-bit Solaris */
typedef long jlong;
#else
typedef long long jlong;
#endif
typedef signed char jbyte;
#endif /* !_JAVASOFT_JNI_MD_H_ */
会发现,其实JNICALL和JNIEXPORT是空定义
可以省略。(可能存在其他用途)
即原方法为:
jstring Java_com_popoaichuiniu_jacy_examplelearning_Command_getStringFromC
(JNIEnv *, jclass, jstring);//注意这里只是声明了函数,并没有实现
//返回值为jstring(对应java String)
//第一个参数:JNIEnv* 是定义任意native函数的第一个参数(包括调用JNI的RegisterNatives函数注册的函数),指向JVM函数表的指针,函数表中的每一个入口指向一个JNI函数,每个函数用于访问JVM中特定的数据结构。
//第二个参数:调用java中native方法的实例或Class对象,如果这个native方法是实例方法,则该参数是jobject,如果是静态方法,则是jclass
//第三个参数:Java对应JNI中的数据类型,Java中String类型对应JNI的jstring类型。(后面会详细介绍JAVA与JNI数据类型的映射关系)
public static native String getStringFromC(String a);
第三个参数对应这里的参数String a
JNIEXPORT 和 JNICALL的作用:
在上篇文章中,我们在将HelloWorld.c编译成动态库的时候,用-I参数包含了JDK安装目录下的两个头文件目录:
[java] view plain copy
gcc -dynamiclib -o /Users/yangxin/Library/Java/Extensions/libHelloWorld.jnilib jni/HelloWorld.c -framework JavaVM -I/
JAVAHOME/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,用于约束函数入栈顺序和堆栈清理的规则。
总结:
当我们熟悉了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);
C语言宏定义
ndk中JNI.h中:
#define JNIEXPORT __attribute__ ((visibility ("default")))
#define JNICALL __NDK_FPABI__