标签:jniexport 名称 byte order tar base 分割 javah cli
最近了解了关于Java JNI接口的一些关于方法注册与签名相关的知识,在此进行一下总结。
使用JNI接口时,我们首先需要把Java方法声明为native:
然后编写对应的C/C++代码,并编译成为动态链接库(.dll或.so),在调用Java方法前载入动态链接库即可调用:
- static {
- System.loadLibrary("native-lib");
- }
那么,Java文件中的native方法是如何与native文件中的方法一一对应的呢?
在此有两种方法:静态注册与动态注册,下面将一一介绍:
静态注册
采用静态注册时,Java层的native方法与native层的方法在名称上具有一一对应的关系,具体要求如下:
native层的方法名为:Java_<包名>_<类名>_<方法名>(__<参数>)
其中,包名使用下划线代替点号进行分割
只有当native方法出现需要重载的时候,native层的方法名后才需要跟上参数(括号里的内容),参数的编写形式与JNI签名相关(后面会介绍)
通常而言,我们可以把native方法集中在一个类中,然后调用:
下面是静态注册的例子:
Java层:
- package com.app.superxlcr.jnitest;
-
-
- public class NativeTest {
-
- public native void f();
-
- public native int f(int a, double b);
-
- public native void f(Object a, String b);
-
- public native void g();
-
- }
native层:
- #include <jni.h>
-
- #ifndef _Included_com_app_superxlcr_jnitest_NativeTest
- #define _Included_com_app_superxlcr_jnitest_NativeTest
- #ifdef __cplusplus
- extern "C" {
- #endif
- JNIEXPORT void JNICALL Java_com_app_superxlcr_jnitest_NativeTest_f__
- (JNIEnv *, jobject);
-
- JNIEXPORT jint JNICALL Java_com_app_superxlcr_jnitest_NativeTest_f__ID
- (JNIEnv *, jobject, jint, jdouble);
-
- JNIEXPORT void JNICALL Java_com_app_superxlcr_jnitest_NativeTest_f__Ljava_lang_Object_2Ljava_lang_String_2
- (JNIEnv *, jobject, jobject, jstring);
-
- JNIEXPORT void JNICALL Java_com_app_superxlcr_jnitest_NativeTest_g
- (JNIEnv *, jobject);
-
- #ifdef __cplusplus
- }
- #endif
- #endif
我们可以看到,对于拥有重载的f 方法,其native方法名称后都带有参数,而没有重载的g 方法则没带有
静态注册JNI方法的弊端非常明显,就是方法名会变得很长,因此下面我们介绍另外一种动态注册的方法
动态注册
使用动态注册时,我们需要准备好需要自己想要对应的native方法,然后构造JNINativeMethod数组,JNINativeMethod是一种结构体,源码如下:
- typedef struct {
-
- const char* name;
-
- const char* signature;
-
- void* fnPtr;
- } JNINativeMethod;
然后重写JNI_OnLoad方法(该方法会在Java层通过System.loadLibrary加载完动态链接库后被调用),我们在其中进行动态注册工作:
- JNIEXPORT jint JNICALL
- JNI_OnLoad(JavaVM* vm, void* reserved) {
- JNIEnv *env = NULL;
- jint result = -1;
-
-
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
-
- return result;
- }
-
-
- const char* className = "com/app/superxlcr/jnitest/MainActivity";
- jclass clazz = env->FindClass(className);
- if (clazz == NULL) {
- return result;
- }
-
-
- if (env->RegisterNatives(clazz, methods, 1) < 0) {
- return result;
- }
-
-
- result = JNI_VERSION_1_4;
- return result;
- }
动态注册的大致步骤如下:
- 通过vm(Java虚拟机)参数获取JNIEnv变量
- 通过FindClass方法找到对应的Java类
- 通过RegisterNatives方法,传入JNINativeMethod数组,注册native函数
对于JNINativeMethod结构而言,签名是其非常重要的一项元素,它用于区分Java中native方法的各种重载形式,下面将介绍方法的签名
方法签名
方法签名对于区分Java层native重载方法有重大意义
总的来说,方法签名的组成规则为:
- (参数类型标识1参数类型标识2...参数类型标识n)返回值类型标识
类型标识对应关系如下:
类型标识 |
Java类型 |
Z |
boolean |
B |
byte |
C |
char |
S |
short |
I |
int |
J |
long |
F |
float |
D |
double |
L包名/类名; |
各种引用类型 |
V |
void |
另外,当Java类型为数组时,在标识前会有“[”符号,例如:String[] 类型标识为 [Ljava/lang/String;
下面举几个例子:
- public native void f();
-
- public native int f(int a, double b);
-
- public native void f(Object a, String b);
-
- public native void g();
- BufferedReader使用readLine问题
-
有时我们在使用BufferedReader时候会发现使用readLine函数迟迟没有任何返回,这是因为BufferedReader和BufferedWriter是基于行进行操作的,因此我们使用BufferedWriter的时候使用newLine函数即可,具体代码如下:
- BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
- writer.write(str);
- writer.newLine();
- writer.flush();
-
- BufferedReader reader = new BufferedReader(new InputStreamReader(in));
- str = reader.readLine();
JNI 方法注册与签名+BufferedReader使用readLine问题
标签:jniexport 名称 byte order tar base 分割 javah cli
原文地址:http://www.cnblogs.com/KingIceMou/p/6976557.html