企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
先来看动态注册中的一段代码: ~~~ tatic JNINativeMethod gMethods[] = { ...... { "processFile" //processFile的签名信息,这么长的字符串,是什么意思? "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", (void*)android_media_MediaScanner_processFile }, ...... } ~~~ 上面代码中的JNINativeMethod已经见过了,不过其中那个很长的字符串"(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V"是什么意思呢? 根据前面的介绍可知,它是Java中对应函数的签名信息,由参数类型和返回值类型共同组成。不过为什么需要这个签名信息呢? - **这个问题的答案比较简单。因为Java支持函数重载,也就是说,可以定义同名但不同参数的函数。但仅仅根据函数名,是没法找到具体函数的。为了解决这个问题,JNI技术中就使用了参数类型和返回值类型的组合,作为一个函数的签名信息,有了签名信息和函数名,就能很顺利地找到Java中的函数了。** JNI规范定义的函数签名信息看起来很别扭,不过习惯就好了。它的格式是: ~~~ (参数1类型标示参数2类型标示...参数n类型标示)返回值类型标示。 ~~~ 来看processFile的例子: ~~~ Java中函数定义为void processFile(String path, String mimeType) 对应的JNI函数签名就是 (Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V 其中,括号内是参数类型的标示,最右边是返回值类型的标示,void类型对应的标示是V。 当参数的类型是引用类型时,其格式是”L包名;”,其中包名中的”.”换成”/”。上面例子中的 Ljava/lang/String;表示是一个Java String类型。 ~~~ 函数签名不仅看起来麻烦,写起来更麻烦,稍微写错一个标点就会导致注册失败。所以,在具体编码时,读者可以定义字符串宏,这样改起来也方便。 表2-3是常见的类型标示: :-: 表2-3 类型标示示意表 | 类型标示 | Java类型 | 类型标示 | Java类型| | --- | --- | --- | --- | | Z | boolean | F | float | | B | byte | D | double | | C| char | L/java/langaugeString; | String | | S | short | [I | int[] | | I | int | [L/java/lang/object; | Object[] | | J | long | | | 上面列出了一些常用的类型标示。请读者注意,如果Java类型是数组,则标示中会有一个“[”,另外,引用类型(除基本类型的数组外)的标示最后都有一个“;”。 再来看一个小例子,如表2-4所示: :-: 表2-4 函数签名小例子 | 函数签名 | Java函数 | | --- | --- | | “()Ljava/lang/String;” | String f() | | “(ILjava/lang/Class;)J” | long f(int i, Class c) | | “([B)V” | void f(byte[] bytes) | 请读者结合表2-3和表2-4左栏的内容写出对应的Java函数。 虽然函数签名信息很容易写错,但Java提供一个叫javap的工具能帮助生成函数或变量的签名信息,它的用法如下: ~~~ javap –s -p xxx。 ~~~ 其中xxx为编译后的class文件,s表示输出内部数据类型的签名信息,p表示打印所有函数和成员的签名信息,而默认只会打印public成员和函数的签名信息。 有了javap,就不用死记硬背上面的类型标示了。