JNI的第2种写法:本地方法注册
声明:迁移自本人CSDN博客https://blog.csdn.net/u013365635
孔乙己说,茴香豆的茴有四种写法,今天谈谈JNI的第2种写法:本地方法注册。
这种写法的好处是不需要使用javah生成一个C++头文件,也不必使用javah自动生成的长长的C++函数名,往往在native函数很多的情况下,扩展比较灵活。
之前的笔者写的文章中介绍的是函数名映射的方法,今天介绍的是采用注册本地方法的方式 。本质都是建立起Java层native函数和C层函数的映射关系。
C++代码部分做了一些额外的测试操作,这些测试操作意在表明Java堆和C本地堆之间的关系。
测试类
package com.testjnitype2;
public class TestJNIType2
{
private static NativeUtils nativeUtils = new NativeUtils();
static
{
System.load("D:\\test\\src\\com\\testjnitype2\\lib_testjni_Type2_amd64.dll");
}
public static void main(String[] args)
{
byte[] dataSharedByJavaC = new byte[10];
System.out.println("JNIType2 before-->dataSharedByJavaC=" + dataSharedByJavaC);
for (int i = 0; i < 10; i++)
{
System.out.print(dataSharedByJavaC[i] + " ");
}
System.out.println("\n----------");
nativeUtils.tranferBytes(dataSharedByJavaC);
System.out.println("JNIType2 after-->dataSharedByJavaC=" + dataSharedByJavaC);
for (int i = 0; i < 10; i++)
{
System.out.print(dataSharedByJavaC[i] + " ");
}
}
}
java native方法定义类
package com.testjnitype2;
public class NativeUtils
{
public native void tranferBytes(byte[] dataSharedByJavaC);
}
C++实现类
#include "jni.h"
#include "stdio.h"
static const char *classPath = "com/testjnitype2/NativeUtils";
int FAILURE = -1;
int SUCESS = 0;
JNIEXPORT void JNICALL tranfer_bytes(JNIEnv *jniEnv, jobject obj, jbyteArray dataSharedByJavaC);
//step1-1:建立java方法与native 方法的映射关系
//JNINativeMethod定义在jni.h中,定义如下
/**
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
*/
static const JNINativeMethod methods[] =
{
/*
tranferBytes为java层定义的方法,tranfer_bytes为native层定义的方法,此处就不需要写成Java_com_testjnitype2_NativeUtils_tranferBytes这种格式了
*/
{"tranferBytes", "([B)V", (void*)tranfer_bytes}
};
//step 1-2:实现与java方法映射到的本地方法
JNIEXPORT void JNICALL tranfer_bytes(JNIEnv *jniEnv, jobject obj, jbyteArray dataSharedByJavaC)
{
printf("\n----------");
//data在native层的地址
printf("\ndataSharedByJavaC addr = %x, dataSharedByJavaC and dataSharedByJavaC addr transfered from java parsed by native is random:\n", dataSharedByJavaC);
for (int i=0; i<10; i++)
{
printf("%d ", dataSharedByJavaC[i]);
}
//data在native层解析后指向java data后的地址
jbyte* p = jniEnv->GetByteArrayElements(dataSharedByJavaC, NULL);
printf("\n----------");
printf("\np addr = %x, make native p point to java heap, now native data pointed by p is definitized, in other wolds, it's java heap data:\n", p);
for (int i=0; i<10; i++)
{
printf("%d ", p[i]);
}
//重新赋值,也就是更改Java heap中的数据
for (int i = 0; i < 10; i++)
{
p[i] = i * i + 1;
}
jniEnv->ReleaseByteArrayElements(dataSharedByJavaC, p, 0);
return;
}
//step2:在jvm中注册映射关系
int registerNativeMethods(JNIEnv *env, const char *classPath)
{
printf("begin register native methods");
jclass clazz = env->FindClass(classPath);
if (!clazz)
{
return FAILURE;
}
if (env->RegisterNatives(clazz, methods, sizeof(methods)/sizeof(JNINativeMethod)) != JNI_OK)
{
return FAILURE;
}
return SUCESS;
}
/**
step3:实现jni.h中声明的方法JNI_OnLoad,并调用step 2中的注册方法
该方法在程序启动时,Java加载本地库的时被调用,比如System.load("xxx");
*/
JNIEXPORT jint JNI_OnLoad(JavaVM *jvm, void* reserved)
{
printf("JNI OnLoad");
JNIEnv *env;
if (jvm->GetEnv((void**)&env, JNI_VERSION_1_8) != JNI_OK)
{
printf("get env error");
return -1;
}
registerNativeMethods(env, classPath);
return JNI_VERSION_1_8;
}
/**
实现jni.h中声明的方法
*/
JNIEXPORT void JNI_OnUnload(JavaVM* jvm, void* reserved)
{
JNIEnv *env;
if (jvm->GetEnv((void**)&env, JNI_VERSION_1_8) != JNI_OK)
{
printf("get env error");
}
}
C代码编译方法
d:\VS2010\Microsoft Visual Studio 10.0\VC>cl /LD D:\test\src\com\testjnitype2\testjnitype2.cpp -o D:\test\src\com\testjnitype2\lib_testjni_Type2_amd64.dll
编译完后目录大概如下图。
执行结果如下
JNIType2 before-->dataSharedByJavaC=[B@78308db1
0 0 0 0 0 0 0 0 0 0
----------
JNIType2 after-->dataSharedByJavaC=[B@78308db1
1 2 5 10 17 26 37 50 65 82
----------
JNI OnLoad
----------
begin register native methods
----------
dataSharedByJavaC addr = 2c5f470, dataSharedByJavaC and dataSharedByJavaC addr transfered from java parsed by native is random:
240 96 213 106 7 0 0 0 88 89
----------
p addr = 1dd95e70, make native p point to java heap, now native data pointed by p is definitized, in other wolds, it's java heap data:
0 0 0 0 0 0 0 0 0 0
执行结果暗含了很多信息,比如,输出的顺序、打印的数组值为什么会是上面的样子?这个会做个专题进行讨论。
再说说jni的使用方法吧,其实,本地方法注册法和函数名映射法可以混合使用的。
顺便说一句,netty集成平台动态库的方法就是用的这种。
以上。
JNI的第2种写法:本地方法注册的更多相关文章
- 字符设备驱动另一种写法—mmap方法操作LED
最近在看韦老师的视频,讲解了很多种字符设备的驱动写法.经过自己的研究之后,我发现还有另外一种写法,直接在应用层操作,省去了内核中的地址映射部分,使得用户可以在应用层直接操作LED. mm ...
- JNI的第1种写法 及 JNI通过形参修改Java数据
声明:迁移自本人CSDN博客https://blog.csdn.net/u013365635 目的:通过形参在Java 和C之间传递数据,尤其是大块的媒体数据 优点:避免通过返回值返回一个巨大的数据块 ...
- 使用JNA替代JNI调用本地方法
JNA全称是Java Native Access,是Sun推出的一种调用本地方法技术,比起它的同门师兄JNI,JNA大大简化了调用本地方法的过程,使用也比较方便, JNA是在JNI的基础上完善的,用青 ...
- Effective Java 第三版——66. 明智谨慎地使用本地方法
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- javascript中面向对象的5种写法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 鼠标放上时显示隐藏的div或者其他代码的“jquery”的三种写法和“JavaScript”的一种写法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Android Studio NDK JNI动态注册本地方法
概述 可能大家觉得javah生成的函数名又臭又长,不太好看.这里可以提供另外一种方法来动态注册c++函数,让其根Java中的native方法关联起来. 实现 这里通过JNIEnv的Resisterna ...
- Android调用JNI本地方法经过有点改变
方法注册好后要经过哪些路 Android一个异常捕获项目 https://github.com/xroche/coffeecatch coffeecatch CoffeeCatch, a tiny n ...
- java 本地方法(JNI)
最近搞了一个调用第三方so库做登录认证的任务,以前对JNI没什么概念,最近学习了 <java核心技术> 本地方法 一章,把自己写的一些例子记录一下. 自己C语言真是渣渣,所以所有的例子都在 ...
随机推荐
- 找不到xml、找不到类
解决java.lang.ClassNotFoundException 解决 java.io.FileNotFoundException: class path resource [xxx.xml] ...
- read和write函数的使用
https://blog.csdn.net/qq_33883085/article/details/88667003
- java核心-多线程-零碎知识收集
1.不能使用Integer作为并发锁 原因:synchronized(Integer)时,当值发生改变时,基本上每次锁住的都是不同的对象实例,想要保证线程安全,推荐使用AtomicInteger之类会 ...
- 一百一十、SAP的OO-ALV之四,定义屏幕相关变量和逻辑流
一.代码如下,定义相关变量 二.来带屏幕页面,双击STATUS_9000和USER_COMMAND_9000,自动生成相应代码 三.点击是 四.会自动生产关联的Includ文件 五.我们自己创建一个M ...
- 二十五、JavaScript之查找字符串中的字符串indexOf和lastIndexOf的用法
一.代码如下 二.效果如下 <!DOCTYPE html> <html> <meta http-equiv="Content-Type" conten ...
- ServletConfig详解
ServletConfig是Servlet中的init()方法的参数类型,服务器会在调用init()方法时传递ServletConfig对象给init()方法. ServletConfig对象封装 ...
- 面向对象-接口(interface)实战案例
面向对象-接口(interface)实战案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.接口(interface)概述 1>.接口的语法格式 接口时抽象类的一种特殊体 ...
- EUI库 - 概述
新特性 36k 访问EUI组件宽高时,也会跟原生显示对象的表现一致,立即能得到包含子项的宽高值 统一的显示列表 普通对象和eui对象都可用addChild来添加 一个逻辑组件只管 ...
- 群论&Polya计数
群论&Polya计数 其实在我听课的过程中,我发现针对于学习OI中的群并没有什么过多必要向内学习... 群 以后会补的. 就是\(QQ\)群. 置换 置换就是一个... \[ \begin{m ...
- Pandas_one-hot encoding与dummy encoding
Pandas_特征编码 one-hot encoding 基本思想是将离散型特征的每一种取值都看成一种状态,保证每一个取值只会使得一种状态处于激活状态. 编码函数pd.get_dummies() du ...