在前面的章节JNI/NDK开发指南之JNI数据类型,描述符详解中我们详细的讲解了数据类型,描述符等相关的概念。如果是刚开始接触JNI的话,读者看到那些概念都会懵逼了,哇开发一个JNI的端咋这么多需要记忆的概念,描述符啊,数据类型啊,签名啊?其实JDK的开发工具为我们提供了一些快捷的工具如javah和javap,这两个工具,都是jdk的bin目录下提供的命令。通过学习这几个命令工具的使用和集成,也为我们后面介绍JNI的动态注册和静态注册打下相关基础。
当在进行JNI开发的时候,当我们便写好了Java端的代码,此时就得编写JNI端的代码了,此时无论你是使用动态注册还是静态注册都会牵涉到Native函数的签名等问题,那么此时我们可以使用到javah的工具了,javah会去搜索这个类里面所有的native方法,然后会帮你生成c++方法的头文件,也就是方法声明。下面我们使用一个简单的案例说明怎么使用javah生成jni的头文件。
通过前面的章节,我们知道javah是JDK开发工具自带的一个命令,我想能看到此处的读者一定是配置好了java的开发编译环境了,不然也不会学习JNI的使用。打开终端,然后输入javah可以看到javah命令的相关参数如下,比较简单
λ javah
用法:
javah [options] <classes>
其中, [options] 包括:
-o <file> 输出文件 (只能使用 -d 或 -o 之一)
-d <dir> 输出目录
-v -verbose 启用详细输出
-h --help -? 输出此消息
-version 输出版本信息
-jni 生成 JNI 样式的标头文件 (默认值)
-force 始终写入输出文件
-classpath <path> 从中加载类的路径
-cp <path> 从中加载类的路径
-bootclasspath <path> 从中加载引导类的路径
<classes> 是使用其全限定名称指定的
(例如, java.lang.Object)。
1.使用Eclipse创建Android工程,在这里我们简单的创建了一个Android工程Android2Native,工程绝对路径位于E:\workspace\Android2Native路径下, 主文件路径位于\src\com\xxx\jni\路径下(路径对后面的javah编译很重要),此时的Java文件为JNI.java:
package com.xxx.jni;
public class JNI {
public native void fun();//native方法
public native static void fun1();//native方法
}
2.编译JNI.java文件,生成JNI.class文件,位于路径E:\workspace\Android2Native\bin\classes\com\xxx\jni\JNI.class。
3.使用javah指令编译JNI.class文件,生成Java与C/C++之间进行通信的约定接口头文件,它规定了Java中nativemethod在C/C++的具体接口。运行cmd后,进入于E:\workspace\Android2Native路径下,使用javah指令,指令命令的格式如下:
//-d .h文件输出目录(输出到Android工程JNI文件所在目录)
//-jni java文件包名
javah -d jni -classpath src -jni com.xxx.jni.JNI
此时在下生成了一个com_xxx_jni_JNI.h,如下所示:
4.查看生成头文件内容
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_xxx_jni_JNI */
#ifndef _Included_com_xxx_jni_JNI
#define _Included_com_xxx_jni_JNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_xxx_jni_JNI
* Method: fun
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_xxx_jni_JNI_fun
(JNIEnv *, jobject);
/*
* Class: com_xxx_jni_JNI
* Method: fun1
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_xxx_jni_JNI_fun1
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
前面的章节讲解了javah的作用用途,那么javap是拿来干什么的?jdk自带的一个反编译工具,也可以查看java编译器生成的字节码,在此我们只利用他查看Java方法签名,这个在JNI中的动态注册中使用比较多。新手开发的时候建议手写,熟悉相关Java函数的签名。
通过前面的章节,我们知道javap是JDK开发工具自带的一个命令,我想能看到此处的读者一定是配置好了java的开发编译环境了,不然也不会学习JNI的使用。打开终端,然后输入javap可以看到javap命令的相关参数如下,比较简单
λ javap
用法: javap <options> <classes>
其中, 可能的选项包括:
-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
-classpath <path> 指定查找用户类文件的位置
-cp <path> 指定查找用户类文件的位置
-bootclasspath <path> 覆盖引导类文件的位置
1.继续使用前面的JNI.java文件为例来说明简单使用,此时的文件如下:
package com.xxx.jni;
public class JNI {
public native void fun();
public native static void fun1();
}
2.编译JNI.java文件,生成JNI.class文件,位于路径E:\workspace\Android2Native\bin\classes\com\xxx\jni\JNI.class。
3.使用javah指令编译JNI.class文件,生成该类中方法的相关签名信息。进入E:\workspace\Android2Native\bin\classes\com\xxx\jni,执行如下指令格式,见证奇迹的时刻到了,生成了相关的签名信息。
λ javap -s JNI.class
Compiled from "JNI.java"
public class com.xxx.jni.JNI {
public com.xxx.jni.JNI();
descriptor: ()V
public native void fun();
descriptor: ()V
public static native void fun1();
descriptor: ()V
}
虽然现在Android studio非常流行,但是由于JNI开发一般是在Android Rom开发环境中用的比较多,所以本人还是喜欢使用Eclipse环境,并且同时Android studio太占内存了,所以我用得不是很多。这里我用Android stuido比较少,并不是开发工具不好,如果单纯做应用开发Android stuido还是非常好的。
1.打开Eclipse,进入如下界面的选项的Externl Tool Configurations选项。
2.进入Externl Tool Configurations面板选项以后,进行如下截图的相关配置
其中Location填入本机jdk环境中的javap.exe的具体位置,Working Directory填入"${project_loc}\src",然后在Arguments中填入“-s -classpath ${project_loc}\bin\classes ${java_type_name}”。
3 然后在Eclipse定位到刚才的JNI.java文件中,然后执行下图所示操作
在Console面版中显示相关的签名信息
1.该集成方法和前面的javap类似,就不过多的讲解了。具体的配置如下所示:
2.执行javah命令,生成JNI.java本地方法对应的头文件,如下截图所示:
可以看到在jni文件中生成了对应的头文件。
好了,JNI/NDK开发指南之javah和javap的使用和集成已经写完了,相关的知识点各位读者都get到了吗。