PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=052)

  本文发布于 2018-01-09 09:13:35,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=052)

环境说明

  无

前言


  由于最近重新接触了部分Android相关的东西,对一些需要混合编程的手段做了整理,同时也对Java中常见的反射技术进行归纳总结。(本文适合知道JNI和反射是什么鬼的人阅读)(可变参数实现在JNI部分的IoctlGpio方法)

Java JNI


  Java native interface(java本地调用接口)主要是实现C或者C++和java交互。

先来看一张来至于网上的图(如果侵权,请联系我,我会第一时间删除)

  此图说明了一个jvm中(一个进程中)所拥有的资源。而JNI就是用来实现调用本地方法的一种方法。而这些本地方法有两种方式可以被注册到jvm中,分别如下:

JNI之静态注册
public class Operate_Gpio {
public native int IoctlGpio(int cmd, String ...arg);
public native int IoctlGpio(int cmd, int ...arg); //java static code
static {
System.loadLibrary("OperateGpio");
}
}
//call IoctlGpio()
Operate_Gpio op_gpio = new Operate_Gpio();
op_gpio.IoctlGpio(2,1,3);
op_gpio.IoctlGpio(2,"a","b");
extern "C"
JNIEXPORT jint JNICALL
Java_JNI_Operate_1Gpio_IoctlGpio(JNIEnv *env, jobject instance, jint cmd,jobjectArray arg) { jboolean iscopy; switch (cmd) {
case 1: {
jint *arg1 = env->GetIntArrayElements((jintArray) arg, 0); NativeLog(env, instance, 'D', "Test string arg1",
Int_to_String(arg1[0]));
NativeLog(env, instance, 'D', "Test string arg2",
Int_to_String(arg1[1]));
env->ReleaseIntArrayElements((jintArray)arg, arg1, 0);
break;
}
case 2: {
jstring arg3 = (jstring) env->GetObjectArrayElement(arg, 0);
jstring arg4 = (jstring) env->GetObjectArrayElement(arg, 1);
NativeLog(env, instance, 'D', "Test string arg3",
env->GetStringUTFChars(arg3, &iscopy));
NativeLog(env, instance, 'D', "Test string arg4",
env->GetStringUTFChars(arg4, &iscopy));
break;
}
default:{
break;
} }
return 0; }

  说明:通过特定的函数命名方式(包名__类名__函数名)(若以上命名中含有_,将会通过在其后加一位数字方式来标识)来注册。当在java层调用System.loadLibrary,jvm会遍历这个so库的所有合法的方法,并放到本地方法栈,以供使用。

  缺点:

  1. 初次call本地函数时,需要先在jni下去搜索相关的jni函数,然后建立jni函数和native函数关系,导致效率降低
  2. 使用规则复杂,不利于开发(不方便)。
JNI之动态注册
public class Operate_Gpio {
public native int TestGpio();
//java static code
static {
System.loadLibrary("OperateGpio");
}
} Operate_Gpio op_gpio = new Operate_Gpio();
op_gpio.TestGpio();
/**
* Table of methods associated with a single class.
*/
/*
*
* typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
* */
// 结构体,分别是java层的函数名称,签名,对应的函数指针
static JNINativeMethod gMethods[] = {
{ "TestGpio", "()I", (void*)TestGpio },//绑定
};
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
} return JNI_TRUE;
} JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL; if ( vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
assert(env != NULL); if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])))
return -1;
/* success -- return valid version number */
return JNI_VERSION_1_4;
}

  说明:

  1. JNINativeMethod保存Java Native函数和JNI函数的对应关系
  2. Java层中调用System.loadLibrary加载so库,并调用JNI_OnLoad开始注册
  3. JNI_OnLoad中调用AndroidRuntime::registerNativeMethods
  4. AndroidRuntime::registerNativeMethods中调用 jniRegisterNativeMethods

  注意:这里有个知识叫做java签名,此签名是指的方法的类型标识,也就是一堆类型简写。(写法:(函数参数列表)返回值)

  下列是常用的类型总结(资料来于网上,若有侵权,请联系我,我第一时间删除):

/*

Java方法签名中特殊字符/字母含义
特殊字符 数据类型 特殊说明
V void 一般用于表示方法的返回值
Z boolean
B byte
C char
S short
I int
J long
F float
D double
[ 数组 以[开头,配合其他的特殊字符,表示对应数据类型的数组,几个[表示几维数组
L全类名; 引用类型 以L开头、;结尾,中间是引用类型的全类名 */

Java Reflect


  程序运行时获取类的属性,也可调用类的方法。可灵活的方便构建出各种复杂的逻辑结构。

C或者C++层反射

int static NativeLog(JNIEnv *env, jobject instance ,char type, std::string tag, std::string msg){ jclass clazz = env->FindClass("android/util/Log") ;
jmethodID jmth_id;
switch (type){ case 'V':
jmth_id = env->GetStaticMethodID(clazz, "v", "(Ljava/lang/String;Ljava/lang/String;)I");
break;
case 'D':
jmth_id = env->GetStaticMethodID(clazz, "d", "(Ljava/lang/String;Ljava/lang/String;)I");
break;
case 'I':
jmth_id = env->GetStaticMethodID(clazz, "i", "(Ljava/lang/String;Ljava/lang/String;)I");
break;
case 'W':
jmth_id = env->GetStaticMethodID(clazz, "w", "(Ljava/lang/String;Ljava/lang/String;)I");
break;
case 'E':
jmth_id = env->GetStaticMethodID(clazz, "e", "(Ljava/lang/String;Ljava/lang/String;)I");
break;
default:
break;
} return env->CallStaticIntMethod(clazz, jmth_id, env->NewStringUTF(tag.c_str()), env->NewStringUTF(msg.c_str()));
} extern "C"
JNIEXPORT jint JNICALL
Java_JNI_Operate_1Gpio_OpenGpio(JNIEnv * env, jobject obj){
//open devices ... ...
NativeLog(env, obj, 'D', "Test", "Reflect test.");
return 0;
}
public class Operate_Gpio {

    public native int OpenGpio();

    // Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("OperateGpio");
}
} Operate_Gpio op_gpio = new Operate_Gpio();
op_gpio.OpenGpio();
JAVA层反射
        try {
Class clazz = Class.forName("android.util.Log");
Method method = clazz.getDeclaredMethod("d", String.class, String.class);
method.invoke(null,"Test","Fuck");//调用静态函数
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}

  所有结果的最终效果如图:

后记


  无

参考文献


打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

Android JNI静态和动态注册 、Java Reflect(C或C++层反射和JAVA层反射)、Java 可变参数(JNI实现)的更多相关文章

  1. Android深入理解JNI(一)JNI原理与静态、动态注册

    前言 JNI不仅仅在NDK开发中应用,它更是Android系统中Java与Native交互的桥梁,不理解JNI的话,你就只能停留在Java Framework层.这一个系列我们来一起深入学习JNI. ...

  2. JNI原理与静态、动态注册

    前言 JNI不仅仅在NDK开发中应用,它更是Android系统中Java与Native交互的桥梁,不理解JNI的话,你就只能停留在Java Framework层.这一个系列我们来一起深入学习JNI. ...

  3. ndk学习20: jni之OnLoad动态注册函数

    一.原理 当在系统中调用System.loadLibrary函数时,该函数会找到对应的动态库, 然后首先试图找到"JNI_OnLoad"函数,如果该函数存在,则调用它 JNI_On ...

  4. Java基础知识强化之集合框架笔记32:集合之可变参数的概述和使用

    1. 可变参数的概述和使用: (1)可变参数:定义方法的时候不知道该定义多少个参数(2)格式:     修饰符  返回值类型  方法名(数据类型… 变量名){   }  注意: 这里的变量其实是一个数 ...

  5. Android(java)学习笔记173:BroadcastReceiver之 静态注册 和 动态注册

    1. 广播接受者>什么是广播.收音机.电台:对外发送信号.收音机:接收电台的信号. >在android系统里面,系统有很多重要的事件: 电池电量低,插入充电器,sd卡被移除,有电话打出去, ...

  6. Android(java)学习笔记116:BroadcastReceiver之 静态注册 和 动态注册

    1. 广播接受者>什么是广播.收音机.电台:对外发送信号.收音机:接收电台的信号. >在android系统里面,系统有很多重要的事件: 电池电量低,插入充电器,sd卡被移除,有电话打出去, ...

  7. Android中BroadcastReceiver的两种注册方式(静态和动态)详解

    今天我们一起来探讨下安卓中BroadcastReceiver组件以及详细分析下它的两种注册方式. BroadcastReceiver也就是"广播接收者"的意思,顾名思义,它就是用来 ...

  8. JNI静态注册与动态注册详解

    JNI注册,是指将java层方法(native关键字修饰的)和C层方法对应起来,以实现java层代码调用c层代码的目的.JNI注册分为静态注册和动态注册两种,静态注册是通过固定格式方法名进行关联,动态 ...

  9. BroadcastReceiver的两种注册方式之------动态注册

    activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&qu ...

  10. Java 中可变参数

    可变参数 Java 中可变参数 现在需要编写一个求和的功能,但是不知道有几个参数,在调用的时候才知道有几个参数,请问这如何实现呢? Java 给我们提供了一个 JDK 1.5 的新特性---可变参数 ...

随机推荐

  1. 物联网浏览器(IoTBrowser)-MQTT协议集成和测试

    一.简介 MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议.它工作在 TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情 ...

  2. spring-cloud 配置管理

    作用: 实现配置热更新 实现网关配置热部署 配置模板 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc ...

  3. 【DC渗透系列DC-4】

    主机发现 arp-scan -l ┌──(root㉿kali)-[~] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:6b: ...

  4. JS Leetcode 208. 实现 Trie (前缀树) 题解分析,第一次了解前缀树(字典树)

    壹 ❀ 引 本题来自LeetCode 208. 实现 Trie (前缀树),难度中等,题目描述如下: Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地 ...

  5. NC24608 [USACO 2011 Ope S]Learning Languages

    题目链接 题目 题目描述 Farmer John's N (2 <= N <= 10,000) cows, conveniently numbered 1..N, are fluent i ...

  6. 48.DRF版本控制

    版本控制 版本控制是前后端分离开发一个非常重要的内容,比如说我们重要服务修改.升级等发生版本变化v1.v2.v3等,但是版本发生了变化比如 v1升级到了v2版本,v1版本还有业务在继续使用,相当于同时 ...

  7. Python Split 函数用法

    一.split函数简介Python中split()函数,具体作用如下: 拆分字符串.通过指定分隔符对字符串进行切片,并返回分割后的字符串列表(list):二.语法split() 方法语法: str.s ...

  8. 按奇偶排序数组II

    按奇偶排序数组II 给定一个非负整数数组A,A中一半整数是奇数,一半整数是偶数. 对数组进行排序,以便当A[i]为奇数时,i也是奇数:当A[i]为偶数时,i也是偶数. 你可以返回任何满足上述条件的数组 ...

  9. U盘安装win7提示缺少所需的CD/DVD驱动器设备驱动程序

    问题: 最近使用U盘启动盘安装win7,系统弹出提示框: 解决方法: U盘别插在usb3.0的口(蓝色),换成一个usb2.0的口就可以了

  10. 压测模式该怎么选?RunnerGo五大压测模式详解

    在做性能测试时需要根据性能需求配置不同的压测模式,如:阶梯模式.使用jmeter时我们需要安装插件来配置测试模式,RunnerGo内嵌了压测模式这一选项更方便使用,今天来看看RunnerGo的几种压测 ...