Register native method - 数据类型和method descriptor

使用JNI时,为了使得虚拟机可以找到在C/C++ code中定义的native方法,有两种机制可以用,一种是通过为native 方法以特定格式命名来实现,另外的一种是所谓的JNI_OnLoad机制。更多信息,可参考《android app中使用JNI》。在JNI_OnLoad机制中,我们需要创建一个映射表,以描述java code中声明的native 方法,与C/C++ code中的实现函数之间的对应关系。如下面的这样:

static JNINativeMethod gMethods[] = {
NATIVE_METHOD(HelloJni, stringFromJNI, "()Ljava/lang/String;"),
NATIVE_METHOD(HelloJni, stringToJNI, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(HelloJni, sumIntWithNative, "([III)I"),
NATIVE_METHOD(HelloJni, sumDoubleWithNative, "([DII)D"),
};

可以看到,这个映射表是一个数组,它的数据元素类型为JNINativeMethod,这个结构的定义如下:

typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;

这个结构有三个成员,name为java code中声明的方法名,fnPtr为函数指针,指向实现了java 的native方法的那个C/C++函数,signature则为方法的签名。name 和fnPtr的含义都清晰明确。而方法的签名究竟是干什么的呢?看上面的gMethods的定义,想必我们大概也可以猜个八九不离十。没错,方法的签名就是用来描述一个方法,它所接受的参数类型,及其返回值类型的。

这个signature,又称为method descriptor。它的定义,究竟又有什么样的规则呢?JNI的Spec中对于这个问题,有如下的一段描述:

  • Method descriptors are formed by placing the field descriptors of all argument types in a pair of parentheses, and following that by the field descriptor of the return type. There are no spaces or other separator characters between the argument types. "V" is used to denote the void method return type. Constructors use "V" as their return type, and use "<init>" as their name.

意思就是说,method descriptor由两个部分组成,一是由小括号包裹的,所有参数类型的field descriptors所组成的部分,其中的这些field descriptors对应于参数的顺序依次排列;二是紧紧跟着的返回值类型的field descriptors。整个的method descriptor中都不包含空格。“V”用于表示void 方法的返回值类型。构造函数用“V”作为他们的返回值类型,并使用“<init>”作为他们的名字。而不接受任何参数的方法,其参数的field descriptors部分则为空,而只留小括号在那里。

接下来我们可以看一下,各种数据类型,它的field descriptors都是些什么鬼东西。首先,可以先来看一下Java的原始数据类型的field descriptors

而对于引用类型,其field descriptor则是以“L”字符开头,后面紧跟着class descriptor,并以“;”字符结尾。一个class descriptor用表示一个类或接口的名称。我们可以从一个类或接口的完全限定名中获取到这个class descriptor,方法为,将完全限定名字串中的“.”字符都用“/”字符来替换,比如java.lang.String 这个类的class descriptor就是"java/lang/String"。数组当然也是引用类型。数组的field descriptor的构成则为“[”字符,后面紧跟着数组成员类型的field descriptor。比如,“int[]”的class descriptor为 "[I", “double[][][]” 的class descriptor为"[[[D"。回到field descriptor,原始数据类型的数组,它们的field descriptor不同于其他的引用类型的地方则在于,其是不需要开头的“L”及后面的“;”字符的。像下面这样:

看一些实际code的例子。首先,是Java code中的native方法的声明:

public native int sumIntWithNative(int[] dataElement, int start, int end);
public native double sumDoubleWithNative (double[] dataElement, int start, int end);
public native String stringToJNI(String text);

然后是native code中,我们所创建的映射表,值得我们重点关注的,是那些个方法的签名与Java code中native 方法的声明中参数及返回值类型之间的对应关系:

static JNINativeMethod gMethods[] = {
NATIVE_METHOD(HelloJni, stringFromJNI, "()Ljava/lang/String;"),
NATIVE_METHOD(HelloJni, stringToJNI, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(HelloJni, sumIntWithNative, "([III)I"),
NATIVE_METHOD(HelloJni, sumDoubleWithNative, "([DII)D"),
};

在Java code中,对于那些native方法的调用:

int[] dataElement = new int[]{2, 3, 4, 6};
int sum = sumIntWithNative(dataElement, 0, dataElement.length);
double[] doubleElement = new double[]{3.4, 5.3, 7.6, 9.2};
double doubleSum = sumDoubleWithNative(doubleElement, 0, doubleElement.length);
stringToJNI("text");

native code中,对于那些native方法的实现:

static jstring HelloJni_stringToJNI( JNIEnv* env, jobject thiz, jstring text)
{
JniDebug("from HelloJni_stringToJNI");
return text;
} static jint HelloJni_sumIntWithNative( JNIEnv* env, jobject thiz,
jintArray dataElement, jint start, jint end) {
JniDebug("from HelloJni_sumIntWithNative");
} static jdouble HelloJni_sumDoubleWithNative( JNIEnv* env, jobject thiz,
jdoubleArray dataElement, jint start, jint end) {
JniDebug("from HelloJni_sumDoubleWithNative");
}

在native 方法的实现中,值得我们关注的是,那些参数的声明。可以看到,Java Language Type用于java code中,native方法声明时描述参数或返回值的类型;field descriptor 用于method descriptor,或称signature中,描述参数或返回值的类型;而Native Type则用于native method 声明中,描述参数或返回值的类型。上面的那段code执行的结果:

此外,android frameworks中JNI code的一些例子,也值得我们参考,如JellyBean/frameworks/base/core/jni/android/graphics/Canvas.cpp这个文件里面的映射表:

{"native_drawPaint","(II)V", (void*) SkCanvasGlue::drawPaint},
{"drawPoint", "(FFLandroid/graphics/Paint;)V",(void*) SkCanvasGlue::drawPoint},
{"drawPoints", "([FIILandroid/graphics/Paint;)V",(void*) SkCanvasGlue::drawPoints},
{"drawLines", "([FIILandroid/graphics/Paint;)V",(void*) SkCanvasGlue::drawLines},
{"native_drawLine","(IFFFFI)V", (void*) SkCanvasGlue::drawLine__FFFFPaint},
{"native_drawRect","(ILandroid/graphics/RectF;I)V",(void*) SkCanvasGlue::drawRect__RectFPaint},
{"native_drawRect","(IFFFFI)V", (void*) SkCanvasGlue::drawRect__FFFFPaint},
{"native_drawOval","(ILandroid/graphics/RectF;I)V",(void*) SkCanvasGlue::drawOval},
{"native_drawCircle","(IFFFI)V", (void*) SkCanvasGlue::drawCircle},
{"native_drawArc","(ILandroid/graphics/RectF;FFZI)V",(void*) SkCanvasGlue::drawArc},
{"native_drawRoundRect","(ILandroid/graphics/RectF;FFI)V",(void*) SkCanvasGlue::drawRoundRect},
{"native_drawPath","(III)V", (void*) SkCanvasGlue::drawPath},
{"native_drawBitmap","(IIFFIIII)V",(void*) SkCanvasGlue::drawBitmap__BitmapFFPaint},
{"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/RectF;III)V",(void*) SkCanvasGlue::drawBitmapRF},
{"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/Rect;III)V",

这个表中,非java 语言内部类的field descriptor,如android.graphics.Paint, android.graphics.RectF等的field descriptor的写法,值得我们参考借鉴。

【转】:https://my.oschina.net/wolfcs/blog/126474

JNI,RegisterNative参数解析的更多相关文章

  1. 写个C#命令行参数解析的小工具

    最近测试工作做的比较多因此时常要创建一些控制台类型的应用程序.因为程序有不同的参数开关,需要在程序启动的时候通过命令行来给程序传递各种开关和参数.直接操作args有些不方便,所以就写了个解析参数的小工 ...

  2. Python--命令行参数解析Demo

    写没有操作界面的程序时,最讨厌的就是参数解析问题,尤其是很多参数那种,下面是一个小Demo,拿出来与各位分享: # -*- coding:utf8 -*- import os import datet ...

  3. Node基础:url查询参数解析之querystring

    模块概述 在nodejs中,提供了querystring这个模块,用来做url查询参数的解析,使用非常简单. 模块总共有四个方法,绝大部分时,我们只会用到 .parse(). .stringify() ...

  4. Zookeeper + Hadoop2.6 集群HA + spark1.6完整搭建与所有参数解析

    废话就不多说了,直接开始啦~ 安装环境变量: 使用linx下的解压软件,解压找到里面的install 或者 ls 运行这个进行安装 yum install gcc yum install gcc-c+ ...

  5. argparse - 命令行选项与参数解析(转)

    argparse - 命令行选项与参数解析(译)Mar 30, 2013 原文:argparse – Command line option and argument parsing 译者:young ...

  6. 一步一步自定义SpringMVC参数解析器

    随心所欲,自定义参数解析器绑定数据. 题图:from Zoommy 干货 SpringMVC解析器用于解析request请求参数并绑定数据到Controller的入参上. 自定义一个参数解析器需要实现 ...

  7. /proc/sys/ 下内核参数解析

    http://blog.itpub.net/15480802/viewspace-753819/ http://blog.itpub.net/15480802/viewspace-753757/ ht ...

  8. ThreadPoolExecutor参数解析

    ThreadPoolExecutor是一个非常重要的类,用来构建带有线程池的任务执行器,通过配置不同的参数来构造具有不同规格线程池的任务执行器. 写在前面的是: 线程池和任务执行器,线程池的定义比较直 ...

  9. Js把URL中的参数解析为一个对象

    <!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> <title&g ...

随机推荐

  1. 第四章 栈与队列(c4)栈应用:中缀表达式求值

  2. 如何利用FPGA进行时序分析设计

    FPGA(Field-Programmable Gate Array),即现场可编程门阵列,它是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的,既解决了定制电路的不足,又克服了原有可编程器 ...

  3. express 学习札记

    Enjoy yourself! 祝你玩得开心! I have no idea. 我没有头绪. I just made it! 我做到了!  I’ll see to it 我会留意的. Express ...

  4. [HDOJ]Coin Change(DP)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2069 题意 有面值1,5,10,25,50的硬币数枚,对于输入的面值n,输出可凑成面值n(且限制总硬笔 ...

  5. 自动化测试之selenium工具简单介绍

    一.selenium简单介绍 1.selenium的成员 2.selenium工作原理 二.webdrive 常见元素定位

  6. f5 SNMP配置

    1.选择监控终端 2.配置团体名称:

  7. List<Map<String, String>>和Map<String, List<String>>遍历

    public void TestM() {     List<Map<String, String>> lm = new ArrayList<>();     Ma ...

  8. ECharts动态获取后台传过来的json数据进行多个折线图的显示,折线的数据由后台传过来

    ECharts 多个折线图动态获取json数据 效果图如下: 一.html部分 <p id="TwoLineChart" style="width:100%; he ...

  9. html标签一

    <body></body> 网页内容 <p></p>段落 <h1></h1> ----<h6></h6> ...

  10. C# 使用printDocument1.Print打印时不显示 正在打印对话框

    C#使用printDocument1.Print打印时不显示正在打印对话框有两种方法 第一种,使用PrintController       PrintController printControll ...