本文共 4938 字,大约阅读时间需要 16 分钟。
JNI开发中,你需要知道的一些建议- https://www.jianshu.com/p/09effb7ecde4
JNI中的方法签名;JNI反射调用Java构造方法、成员方法和静态方法。
> 深入理解JNI-
JNI/Native(so文件): Java程序中的函数可以调用Native语言写的函数,Native一般是指C/C++编写的函数; Native程序中的函数可以调用Java层的函数,也就是说C/C++程序可以调用Java函数(反射)。 通过JNI可以将底层Native世界和java世界联系起来。 JNI的基本数据类型和引用类型。 基本数据类型:jboolean jbyte jchar jshort jint jlong jfloat jdouble 引用类型:jobject jclass jstring jobjectArray jbooleanArray jbyteArray jthrowable JNI中提供三种类型的引用来解决垃圾回收问题: 1.Local Reference:本地引用,一旦JNI层函数返回,这些jobject就可能被垃圾回收 2.Global Reference:全局引用,不主动释放,永远不会被回收 3.Weak Global Reference:弱全局引用,在运行过程中可能会被垃圾回收,因此在使用之前,需要调用JNIEnv的isSameObject判断是否被回收 JNI签名: 如在[MedaiScanner.java]processFile函数定义 private native void processFile(String path, String mimeType, MediaScannerClient client);` 对应的JNI函数签名是 (Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V 在JNI中除了基本类型数组、Class、String和Throwable外其余所有Java对象的数据类型在JNI中都用jobject表示。 操作jobject的本质就是操作这些对象的成员变量和成员函数。在JNI中使用jfieldID和jmethodID来表示Java类的成员变量和成员函数。-- JNI开发学习之C反射调用java方法,JNI中的反射原理
JNI C反射调用java方法- https://www.jianshu.com/p/4893848a3249 JNI 之C反射调用Java- https://github.com/maoqitian/CcallJava> JNI签名
为什么会有方法签名这种东西呢?这是因为Java这边支持函数重载,即虽然参数不一样,但是方法名一样,那么在JNI层它们的方法名都会是一样的,那JNI也会犯迷糊了,得找哪个呢?
不过也正是因为其参数类型是不一样的,所以就出现了方法签名,利用方法签名和方法名来唯一确定一个JNI函数的调用。 既然方法签名是基于参数类型的不同而形成的,首先要知道Java各数据类型对应的签名是什么,也就是所谓的类型签名, 在jni.h文件中就已经定义了这样一套规则,如下:“函数签名”在 NDK开发中很常见,由于Java支持重载,仅靠函数名无法唯一确定一个方法。因此,JNI提供了一套签名规则,用一个字符串来唯一确定一个Java端定义的Native方法。
具体每一种Java数据类型对应的签名字符串如下所示(来自官网JNI的介绍):
原理其实并不复杂,每种基本类型对应一个单字符签名,而类则对应为"L"+类的全路径+";",数组类型则对应"["+元素类型的签名,函数的签名则是:(各参数类型签名)+ 返回类型的签名。
搞清楚了基本原理,我们就可以尝试自定义一个Java工具类,为Java的Native函数生成签名字符串了,具体代码如下:
/*
* COPYRIGHT NOTICE * Copyright (C) 2014, ticktick <lujun.hust@gmail.com> * http://www.linuxidc.com/ * * @license under the Apache License, Version 2.0 * * @file SignatureGen.java * @brief Implement a java class for jni signature generate * * @version 1.0 * @author ticktick * @date 2014/12/15 * */ package com.ticktick.library; import java.util.HashMap; public class SignatureGen { public static final HashMap<String,String> Primitives = new HashMap<String, String>(); static { Primitives.put(Void.class.getName(),"V"); Primitives.put(Boolean.class.getName(),"Z"); Primitives.put(Byte.class.getName(),"B"); Primitives.put(Character.class.getName(),"C"); Primitives.put(Short.class.getName(),"S"); Primitives.put(Integer.class.getName(),"I"); Primitives.put(Long.class.getName(),"J"); Primitives.put(Float.class.getName(),"F"); Primitives.put(Double.class.getName(),"D"); } public static String getSignature( Class ret, Class...params ) { StringBuilder builder = new StringBuilder(); builder.append("("); for( Class param : params ) { builder.append(getSignature(param)); } builder.append(")"); builder.append(getSignature(ret)); return builder.toString(); } protected static String getSignature( Class param ) { StringBuilder builder = new StringBuilder(); String name = ""; if( param.isArray() ) { name = param.getComponentType().getName(); builder.append("["); } else { name = param.getName(); } if( Primitives.containsKey(name) ) { builder.append(Primitives.get(name)); } else { builder.append("L"+name.replace(".","/")+";"); } return builder.toString(); } }该SignatureGen类提供一个支持变参的函数getSignature来获取一个Java函数的签名字符串,第一个参数为函数返回值类型的class对象,变参为每一个函数参数类型的class对象。
具体用法示例如下,打印出不同类型的函数的签名字符串。
Log.d("Signature","void func() --> " + SignatureGen.getSignature(Void.class));
Log.d("Signature","boolean func() --> " + SignatureGen.getSignature(Boolean.class)); Log.d("Signature","int func(boolean a) --> " + SignatureGen.getSignature(Integer.class,Boolean.class)); Log.d("Signature","int func(boolean a,String b) --> " + SignatureGen.getSignature(Integer.class,Boolean.class,String.class)); Log.d("Signature","int func(byte[] c) --> " + SignatureGen.getSignature(Integer.class,Byte[].class)); Log.d("Signature","long func(int n,String str,int arr) -->" + SignatureGen.getSignature(Long.class,Integer.class,String.class,Integer[].class));输出结果截屏如下:
关于JNI函数签名生成器就介绍到这儿了,原理并不复杂所以我也没有进行过多的分析,希望这个工具类能够在大家今后的项目中派上用场,有任何疑问欢迎留言或者来信lujun.hust@gmail.com交流。
--------------
有了数据类型之间的对应关系,JNI就可以正确识别并转换Java类型。那JNI又是如何识别Java的方法呢?
Java支持方法重载,仅靠函数名是无法唯一确定一个方法的。于是JNI提供了一套签名规则,用一个字符串来唯一确定一个方法。其规则如下:
(参数1类型签名参数2类型签名……参数n类型签名)返回值类型签名
以上签名字符串之间均没有空格。
类型签名又有一些规则,如表2-3所示。
表2-3 JNI类型签名规则
(续) 注意 类的签名规则是:“L+全限定类名+;” 三部分组成,其中全限定类名以”/”分隔,而不是用“.”或者“_”分隔。例如,Java 方法:
其方法签名:
括号里面的内容分成三部分,之间没有空格,即“I”、“Ljava/lang/String;”和“[I”,分别代表 int、String和int[]。括号外面是返回值类型签名,J代表long型。
回到Log系统的例子,JNINativeMethod结构体中第二个元素便是方法签名信息, 代码如下:
可以看出isLoggable函数有两个参数,一个是String类型,另一个是int类型,返回值为boolean类型。
至此,我们已经可以正确识别类型信息和函数信息。可如何操作对象并访问它们的成员变量和方法呢?下一节继续介绍。
转载地址:http://ptyk.baihongyu.com/