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语言真是渣渣,所以所有的例子都在 ...
随机推荐
- Vulkan SDK 之 Shaders
Compiling GLSL Shaders into SPIR-V 1.SPIR-V 是vulkan的底层shader语言.GLSL可以通过相关接口转换为SPIR-V. Creating Vulka ...
- 七十二、SAP中内表的修改,添加条件语句,多条目修改
一.代码如下 二.MODIFY执行前断点数据 三.查看到数据如下 四.往下单步走一步,发现有3条数据被修改 五.执行后修改如下
- C# 基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系。
今天写程序的时候调用到一个第三方的DLL文件,本机调试一切都正常,但是程序不是到服务器以后一直提示一个BUG:"基础连接已经关闭: 未能为SSL/TLS 安全通道建立信任关系". ...
- Essay引用如何最大限度的降低抄袭率
今天要说到让无数人恨得要死.为了降重改的哭天喊地的“Paraphrase”.毕竟引用不是打两个引号复制粘贴就能凑字数完事的,无论国内外,都有查重率这个大敌在等着你.想要引用别人的论点论据,就少不了需要 ...
- ACM-DFS Template
自己写的DFSTemplate: // DFS_Template.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" //DFS的思想是:一直向 ...
- POJ 2443:Set Operation 经典位运算好题
Set Operation Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 2965 Accepted: 1196 Des ...
- Distributed--2PC和3PC
参考 https://blog.csdn.net/lnho2015/article/details/78685503 https://www.cnblogs.com/hubaoxi/p/6867203 ...
- Java虚拟机之内存模型
一.java并发基础 在并发编程中存在两个关键问题①线程之间如何通信 ②线程之间如何同步. 通信 通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. ...
- 18 react react-redux 的编写 TodoList
1. 安装 react-redux yarn add react-redux 2. react-redux 编写 TodoList 使所有子组件 都能使用 store #index.js import ...
- CodeForces - 446A DZY Loves Sequences(dp)
题意:给定一个序列a,求最长的连续子序列b的长度,在至多修改b内一个数字(可修改为任何数字)的条件下,使得b严格递增. 分析: 1.因为至多修改一个数字,假设修改a[i], 2.若能使a[i] < ...