最近了解了关于JavaJNI接口的一些关于方法注册与签名相关的知识,在此进行一下总结。

使用JNI接口时,我们首先需要把Java方法声明为native:

  1. public native void f();

然后编写对应的C/C++代码,并编译成为动态链接库(.dll或.so),在调用Java方法前载入动态链接库即可调用:

  1. static {
  2. System.loadLibrary("native-lib");
  3. }

那么,Java文件中的native方法是如何与native文件中的方法一一对应的呢?

在此有两种方法:静态注册与动态注册,下面将一一介绍:

静态注册

采用静态注册时,Java层的native方法与native层的方法在名称上具有一一对应的关系,具体要求如下:
native层的方法名为:Java_<包名>_<类名>_<方法名>(__<参数>)
其中,包名使用下划线代替点号进行分割
只有当native方法出现需要重载的时候,native层的方法名后才需要跟上参数(括号里的内容),参数的编写形式与JNI签名相关(后面会介绍)
通常而言,我们可以把native方法集中在一个类中,然后调用:
  1. javah -jni 包名.类名

自动生成对应的c层头文件

下面是静态注册的例子:
Java层:
  1. package com.app.superxlcr.jnitest;
  2. /**
  3. * Created by superxlcr on 2017/5/25.
  4. */
  5. public class NativeTest {
  6. public native void f();
  7. public native int f(int a, double b);
  8. public native void f(Object a, String b);
  9. public native void g();
  10. }

native层:

  1. /* DO NOT EDIT THIS FILE - it is machine generated */
  2. #include <jni.h>
  3. /* Header for class com_app_superxlcr_jnitest_NativeTest */
  4. #ifndef _Included_com_app_superxlcr_jnitest_NativeTest
  5. #define _Included_com_app_superxlcr_jnitest_NativeTest
  6. #ifdef __cplusplus
  7. extern "C" {
  8. #endif
  9. /*
  10. * Class:     com_app_superxlcr_jnitest_NativeTest
  11. * Method:    f
  12. * Signature: ()V
  13. */
  14. JNIEXPORT void JNICALL Java_com_app_superxlcr_jnitest_NativeTest_f__
  15. (JNIEnv *, jobject);
  16. /*
  17. * Class:     com_app_superxlcr_jnitest_NativeTest
  18. * Method:    f
  19. * Signature: (ID)I
  20. */
  21. JNIEXPORT jint JNICALL Java_com_app_superxlcr_jnitest_NativeTest_f__ID
  22. (JNIEnv *, jobject, jint, jdouble);
  23. /*
  24. * Class:     com_app_superxlcr_jnitest_NativeTest
  25. * Method:    f
  26. * Signature: (Ljava/lang/Object;Ljava/lang/String;)V
  27. */
  28. JNIEXPORT void JNICALL Java_com_app_superxlcr_jnitest_NativeTest_f__Ljava_lang_Object_2Ljava_lang_String_2
  29. (JNIEnv *, jobject, jobject, jstring);
  30. /*
  31. * Class:     com_app_superxlcr_jnitest_NativeTest
  32. * Method:    g
  33. * Signature: ()V
  34. */
  35. JNIEXPORT void JNICALL Java_com_app_superxlcr_jnitest_NativeTest_g
  36. (JNIEnv *, jobject);
  37. #ifdef __cplusplus
  38. }
  39. #endif
  40. #endif

我们可以看到,对于拥有重载的f 方法,其native方法名称后都带有参数,而没有重载的g 方法则没带有

静态注册JNI方法的弊端非常明显,就是方法名会变得很长,因此下面我们介绍另外一种动态注册的方法
 

动态注册

使用动态注册时,我们需要准备好需要自己想要对应的native方法,然后构造JNINativeMethod数组,JNINativeMethod是一种结构体,源码如下:
  1. typedef struct {
  2. // Java层native方法名称
  3. const char* name;
  4. // 方法签名
  5. const char* signature;
  6. // native层方法指针
  7. void*       fnPtr;
  8. } JNINativeMethod;

然后重写JNI_OnLoad方法(该方法会在Java层通过System.loadLibrary加载完动态链接库后被调用),我们在其中进行动态注册工作:

  1. JNIEXPORT jint JNICALL
  2. JNI_OnLoad(JavaVM* vm, void* reserved) {
  3. JNIEnv *env = NULL;
  4. jint result = -1;
  5. // 获取JNI env变量
  6. if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
  7. // 失败返回-1
  8. return result;
  9. }
  10. // 获取native方法所在类
  11. const char* className = "com/app/superxlcr/jnitest/MainActivity";
  12. jclass clazz = env->FindClass(className);
  13. if (clazz == NULL) {
  14. return result;
  15. }
  16. // 动态注册native方法
  17. if (env->RegisterNatives(clazz, methods, 1) < 0) {
  18. return result;
  19. }
  20. // 返回成功
  21. result = JNI_VERSION_1_4;
  22. return result;
  23. }

动态注册的大致步骤如下:

  1. 通过vm(Java虚拟机)参数获取JNIEnv变量
  2. 通过FindClass方法找到对应的Java类
  3. 通过RegisterNatives方法,传入JNINativeMethod数组,注册native函数
对于JNINativeMethod结构而言,签名是其非常重要的一项元素,它用于区分Java中native方法的各种重载形式,下面将介绍方法的签名
 

方法签名

方法签名对于区分Java层native重载方法有重大意义
总的来说,方法签名的组成规则为:
  1. (参数类型标识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;

 
下面举几个例子:
  1. // Signature: ()V
  2. public native void f();
  3. // Signature: (ID)I
  4. public native int f(int a, double b);
  5. // Signature: (Ljava/lang/Object;Ljava/lang/String;)V
  6. public native void f(Object a, String b);
  7. // Signature: ()V
  8. public native void g();
      1. BufferedReader使用readLine问题
      2. 有时我们在使用BufferedReader时候会发现使用readLine函数迟迟没有任何返回,这是因为BufferedReader和BufferedWriter是基于行进行操作的,因此我们使用BufferedWriter的时候使用newLine函数即可,具体代码如下:

        1. BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
        2. writer.write(str);
        3. writer.newLine();
        4. writer.flush();
        5. BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        6. str = reader.readLine();

JNI 方法注册与签名+BufferedReader使用readLine问题的更多相关文章

  1. Dalvik虚拟机JNI方法的注册过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8923483 在前面一文中,我们分析了Dalvi ...

  2. JNI 函数注册与管理

    class<--> 一一对应so-->method     每个so对应于一个类对象 类中的每个native方法对应 于so中的一个native的function,对应关系涉及 {c ...

  3. Android Studio NDK JNI动态注册本地方法

    概述 可能大家觉得javah生成的函数名又臭又长,不太好看.这里可以提供另外一种方法来动态注册c++函数,让其根Java中的native方法关联起来. 实现 这里通过JNIEnv的Resisterna ...

  4. Java Native Interface 五 JNI里的多线程与JNI方法的注册

    本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 JNI里的多线程 在本地方法里写有关多线程的 ...

  5. Android有关JNI 学习(两)为JNI方法名称,数据类型和方法签名的一些知识

    我们知道,使用javah产生c/c++当在头文件,将java定义 native 功能,以产生相应jni层功能,如下面: /* * Class: com_lms_jni_JniTest * Method ...

  6. JNI动态注册native方法及JNI数据使用

    前言 或许你知道了jni的简单调用,其实不算什么百度谷歌一大把,虽然这些jni绝大多数情况下都不会让我们安卓工程师来弄,毕竟还是有点难,但是我们还是得打破砂锅知道为什么这样干吧,至少也让我们知道调用流 ...

  7. Java中使用BufferedReader的readLine()方法和read()方法来读取文件内容

    目标:读文件 编程时,有很多时候需要读取本地文件,下面介绍一下读取方式: 读单行文件 package com; import java.io.*; import java.util.ArrayList ...

  8. JNI的第2种写法:本地方法注册

    声明:迁移自本人CSDN博客https://blog.csdn.net/u013365635 孔乙己说,茴香豆的茴有四种写法,今天谈谈JNI的第2种写法:本地方法注册. 这种写法的好处是不需要使用ja ...

  9. 技术转载:Jni学习四:如何编写jni方法

    转载:http://blog.chinaunix.net/u1/38994/showart_1099528.html 一.概述: 在这篇文章中将会简单介绍如何编制一些简单的JNI 方法.我们都知道JN ...

随机推荐

  1. restful架构风格设计准则(一)以资源为中心、自描述的请求响应、资源状态迁移为粒度

    读书笔记,原文链接:http://www.cnblogs.com/loveis715/p/4669091.html,感谢作者! 一.需求描述 当用户在某个电子商务网站购物时,他首先查看要购买的商品分类 ...

  2. Object.defineProperties()和Object.defineProperty()方法

    Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象. 语法:Object.defineProperty(obj, pro ...

  3. (Sqlyog或Navicat不友好处)SHOW ENGINE INNODB STATUS 结果为空或结果为=====================================

    因为最近在学习innodb引擎,所以就在自己的sqlyog上执行上述命令: SHOW ENGINE INNODB STATUS 结果显示如下: 换了个客户端navicat,执行如下: 最后登录到服务器 ...

  4. Python/MySQL(四、MySQL数据库操作)

    Python/MySQL(四.MySQL数据库操作) 一.数据库条件语句: case when id>9 then ture else false 二.三元运算: if(isnull(xx)0, ...

  5. MSIL实用指南-闭包的生成和调用

    闭包(Closure)是词法闭包(Lexical Closure)的简称.对闭包的具体定义有很多种说法,这些说法大体可以分为两类: 一种说法认为闭包是符合一定条件的函数,比如参考资源中这样定义闭包:闭 ...

  6. 使用控制台调试WinForm窗体程序

    .程序代码结构 .Win32DebuggerHelper.cs using System.Runtime.InteropServices; /* TODO:使用方法 Win32.AllocConsol ...

  7. java String的各种方法及操作

    No. 方法名称 功能 字符与字符串 01 public String(char[] value) 将字符数组中所有内容变为字符串 02 public String(char[] value,int ...

  8. no zuo no die

    #include <iostream> #include <cstring> #include <cstdio> using namespace std; name ...

  9. 计蒜客NOIP模拟赛4 D1T1 小X的质数

    小 X 是一位热爱数学的男孩子,在茫茫的数字中,他对质数更有一种独特的情感.小 X 认为,质数是一切自然数起源的地方. 在小 X 的认知里,质数是除了本身和 1以外,没有其他因数的数字. 但由于小 X ...

  10. [HNOI2012]永无乡

    题目描述 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达 ...