声明:迁移自本人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种写法:本地方法注册的更多相关文章

  1. 字符设备驱动另一种写法—mmap方法操作LED

    最近在看韦老师的视频,讲解了很多种字符设备的驱动写法.经过自己的研究之后,我发现还有另外一种写法,直接在应用层操作,省去了内核中的地址映射部分,使得用户可以在应用层直接操作LED.        mm ...

  2. JNI的第1种写法 及 JNI通过形参修改Java数据

    声明:迁移自本人CSDN博客https://blog.csdn.net/u013365635 目的:通过形参在Java 和C之间传递数据,尤其是大块的媒体数据 优点:避免通过返回值返回一个巨大的数据块 ...

  3. 使用JNA替代JNI调用本地方法

    JNA全称是Java Native Access,是Sun推出的一种调用本地方法技术,比起它的同门师兄JNI,JNA大大简化了调用本地方法的过程,使用也比较方便, JNA是在JNI的基础上完善的,用青 ...

  4. Effective Java 第三版——66. 明智谨慎地使用本地方法

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  5. javascript中面向对象的5种写法

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. 鼠标放上时显示隐藏的div或者其他代码的“jquery”的三种写法和“JavaScript”的一种写法

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. Android Studio NDK JNI动态注册本地方法

    概述 可能大家觉得javah生成的函数名又臭又长,不太好看.这里可以提供另外一种方法来动态注册c++函数,让其根Java中的native方法关联起来. 实现 这里通过JNIEnv的Resisterna ...

  8. Android调用JNI本地方法经过有点改变

    方法注册好后要经过哪些路 Android一个异常捕获项目 https://github.com/xroche/coffeecatch coffeecatch CoffeeCatch, a tiny n ...

  9. java 本地方法(JNI)

    最近搞了一个调用第三方so库做登录认证的任务,以前对JNI没什么概念,最近学习了 <java核心技术> 本地方法 一章,把自己写的一些例子记录一下. 自己C语言真是渣渣,所以所有的例子都在 ...

随机推荐

  1. 无法启动APK安装也,报异常FileUriExposedException

    无法打开APK安装页,报异常FileUriExposedException, https://juejin.im/entry/58e4643db123db15eb79a902

  2. flink笔记(三) flink架构及运行方式

    架构图 Job Managers, Task Managers, Clients JobManager(Master) 用于协调分布式执行.它们用来调度task,协调检查点,协调失败时恢复等. Fli ...

  3. Node.js NPM 作用

    章节 Node.js NPM 介绍 Node.js NPM 作用 Node.js NPM 包(Package) Node.js NPM 管理包 Node.js NPM Package.json NPM ...

  4. jupiter的@TempDir 等不生效

    jupiter与junit是 完全独立的测试组件,要严防在测试中将二者混用.最好在依赖引入jupiter 时 就将junit的依赖干掉,以防在写测试用例时将二者混用.不会报错,但是会导致 jupite ...

  5. 批量go get 代码

    批量 go get 代码 Go

  6. UVA - 11400 Lighting System Design(照明系统设计)(dp)

    题意:共有n种(n<=1000)种灯泡,每种灯泡用4个数值表示.电压V(V<=132000),电源费用K(K<=1000),每个灯泡的费用C(C<=10)和所需灯泡的数量L(1 ...

  7. 知乎live - 三年从前端小工到架构

    王利华   刚毕业 在高德 携程 淘宝    0-3年如何发展 1 技能和能力的区别    css js 抽象     切勿好高骛远 要重视基础 2 人和人的差距是什么    注意个人品牌    提高 ...

  8. jQuery元素的左右移动

    1.下载jQuery,并导入:https://blog.csdn.net/weixin_44718300/article/details/88746796 2.代码实现: <!DOCTYPE h ...

  9. consul配置

    参考:https://blog.csdn.net/achenyuan/article/details/80389410 gcp: consul安装目录:/usr/local/bin/consul co ...

  10. 利用京东云Serverless服务快速构建5G时代的IoT应用

    10月31日,在2019年中国国际信息通信展览会上,工信部宣布:5G商用正式启动.5G商用时代来了! 5G的商用,使得数据传输速度.响应速度.连接数据.数据传输量.传输可靠性等方面都有了显著的提升,这 ...