JNI签名及深入理解JNI,JNI中的反射 - android

JNI开发中,你需要知道的一些建议- https://www.jianshu.com/p/09effb7ecde4

JNI中的方法签名;JNI反射调用Java构造方法、成员方法和静态方法。

> 深入理解JNIhttps://blog.csdn.net/xsf50717/article/details/51598748
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文件中就已经定义了这样一套规则,如下:

    “函数签名”在Android NDK开发中很常见,由于Java支持重载,仅靠函数名无法唯一确定一个方法。因此,JNI提供了一套签名规则,用一个字符串来唯一确定一个Java端定义的Native方法。

具体每一种Java数据类型对应的签名字符串如下所示(来自Oracle官网JNI的介绍):

Android开发实践: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));

输出结果截屏如下:

Android开发实践:JNI函数签名生成器

关于JNI函数签名生成器就介绍到这儿了,原理并不复杂所以我也没有进行过多的分析,希望这个工具类能够在大家今后的项目中派上用场,有任何疑问欢迎留言或者来信lujun.hust@gmail.com交流。

--------------

 

有了数据类型之间的对应关系,JNI就可以正确识别并转换Java类型。那JNI又是如何识别Java的方法呢?

Java支持方法重载,仅靠函数名是无法唯一确定一个方法的。于是JNI提供了一套签名规则,用一个字符串来唯一确定一个方法。其规则如下:

(参数1类型签名参数2类型签名……参数n类型签名)返回值类型签名

以上签名字符串之间均没有空格。

类型签名又有一些规则,如表2-3所示。

表2-3 JNI类型签名规则


(续)


注意 类的签名规则是:“L+全限定类名+;” 三部分组成,其中全限定类名以”/”分隔,而不是用“.”或者“_”分隔。

例如,Java 方法:

 

 
  1. long fun (int n, String str, int[] arr); 

其方法签名:

 

 
  1. (ILjava/lang/String;[I)J 

括号里面的内容分成三部分,之间没有空格,即“I”、“Ljava/lang/String;”和“[I”,分别代表 int、String和int[]。括号外面是返回值类型签名,J代表long型。

回到Log系统的例子,JNINativeMethod结构体中第二个元素便是方法签名信息, 代码如下:

 

 
  1. static JNINativeMethod gMethods[] = {  
  2.    { "isLoggable", "(Ljava/lang/String;I)Z",(void*)  
  3.   android_util_Log_isLoggable },  
  4.    { "println_native","(IILjava/lang/String;Ljava/lang/String;)I",(void*)  
  5.  
  6.   android_util_Log_println_native },  
  7. };  

可以看出isLoggable函数有两个参数,一个是String类型,另一个是int类型,返回值为boolean类型。

至此,我们已经可以正确识别类型信息和函数信息。可如何操作对象并访问它们的成员变量和方法呢?下一节继续介绍。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值