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. Linux 字符设备驱动及一些简单的Linux知识

    一.linux系统将设备分为3类:字符设备.块设备.网络设备 1.字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见 ...

  2. vue-router导航守卫,限制页面访问权限

    在项目开发过程中,经常会需要登录.注册.忘记密码等,也有很多页面是需要登录后才能访问,有些页面是无需登录就可以访问的,那么vue是怎么来限制这些访问权限问题的呢? vue-router导航守卫的bef ...

  3. Mybatis抛出 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@f54509]异常

    今天在做Springmvc和spring 时 mybatis 是抛出异常 Closing non transactional SqlSession [org.apache.ibatis.session ...

  4. Codeforces Beta Round #42 (Div. 2)

    Codeforces Beta Round #42 (Div. 2) http://codeforces.com/contest/43 A #include<bits/stdc++.h> ...

  5. js正则表达式中的正向肯定预查和正向否定预查

    对于没有使用过这几个表达式的人,应该对这个概念都有点不太理解,下面就以实际例子说明这几个表达式的用户. 一.?:pattern——匹配检验:会作为匹配校验,是一个非获取匹配,并出现在匹配字符结果里面, ...

  6. 以太坊博弈游戏 -- FOMO3D,讽刺人性

    以下资料收录自各个博客,下面给出链接. 总的来说,这是基于以太坊的“菠菜”游戏,大资金盘,亏多盈少,大家看看就好. 官网地址:http://exitscam.me/play 出现 7月16日,当时这个 ...

  7. 测试用户体验相关——UI设计准则及方法

    之前跟我们uxc同学聊过一些,记录下来,方便在工作中不断渗透深入和理解,能够逐渐养成比较好的审美和对UI交互问题的敏锐的觉察力. 以问题为导向来吧... 第一个问题:一个menu中的图标一定要风格一致 ...

  8. stark组件之批量操作【模仿Django的admin】

    一.先看下django的admin是如何实现批量操作 首先在配置类中定义一个函数 然后我们为这个函数对象设置一个属性,这个属性主要用来显示在select标签中显示的文本内容 最后把函数对象放到一个ac ...

  9. redis 集群java.lang.NoSuchMethodError:SpringJAR包版本冲突错误解决方法

      项目中出现如下错误,记录下解决方法: org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exce ...

  10. ScrollView嵌套LinearLayout布局不能撑满全屏的问题

    当ScrollView里的元素想填满ScrollView时,使用"fill_parent"或者"match_parent"是不管用的,必需为ScrollView ...