教程 | 入门纯手写JNI编译与使用(适合做demo)

4 篇文章 0 订阅
1 篇文章 0 订阅

前言

做开发的,喜欢看源码的同学,肯定会遇到native关键词,接着就会卡在这里进行不下去,因为native对应的代码就是C了,如果要看再底层的代码,就之只能上官网要C层面的代码了。
2年前,笔者开发过JNI(Java Native Interface),java和C都要实现,这里拿出了笔者多年前的笔记,加点润色说明(貌似也没润多少),希望对大家有所帮助。
目标:

  1. 理解native是什么?
  2. 自己如何实现jni调用?

了解什么是native

我们在学习JVM运行数据区的时候,会了解到虚拟机栈和本地方法栈,我自己做笔记、画图做总结的时候,喜欢把这两个划分到一块,这里可以参考下面的图。
JVM运行数据区

那么JNI是什么?为什么会有native关键词?

JNI(Java Native Interface)调用属于线程私有,用于本地方法的运行,一个Native Method就是一个java调用非java代码的接口,一般来说就是C实现了。

为什么需要Java调用C实现?

有时候Java不能提供的功能(例如类加载机制中,bootstrap的作用是加载加载$JAVA_HOME/jre/lib/下核心类库,如rt.jar,而底层源码hotspot,jvm中是由C++实现),或者是由C来处理更方便更快,那么就需要native方法。
java中bootstrap加载核心类,底层源码可以参考查看: jdk8-hotspot,个人认为,有时候看看底层源码,又能理解出一些新知识,有能力的可以多看看。

正片:一个demo

1.准备好环境

准备好JDK,安装好JDK,配备好环境(这里就不加累述)

2.手打一个JNI的java文件

创建一个TestJNI.java文件:
1)vim TestJNI.java
2)按 i 编写(编写了加载so文件的加载路径,与对应的native方法)
3)按ESC键,:wq! 保存退出
手打JNI的Java环境

public class TestJNI {
	public static native void printTest(String content);
	static {
	/**这是默认读取的加载路径*/
	//	System.loadLibrary("TestJNI");  
	/**指定加载动态库so的路径*/
		System.load("/home/aguicode/programmer/java_project/libTestJNI.so");
	 }
		
	public static void main(String[] args){
		String content="Success";
		printTest(content);
		return;
	}
}
3. 编译TestJNI.java文件
javac TestJNI.java	# 生成class文件

如果遇到权限不够的问题:
1)su 转换为root权限

4.生成TestJNI.h的头文件
javah TestJNI	#  会自动生成C需要的头文件,接着在C实现对应cpp就行啦

TestJNI头文件
PS: 当然也可以自己按照以上规范自己码好.h头文件。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestJNI */

#ifndef _Included_TestJNI
#define _Included_TestJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     TestJNI
 * Method:    printTest
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_TestJNI_printTest
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
5. 创建TestJNI.c文件

1)vim TestJNI.c
2)按 i 编写
创建对应的实现

#include <jni.h>
#include <stdio.h>
#include <TestJNI.h>

JNIEXPORT void JNICALL Java_TestJNI_printTest
  (JNIEnv * env, jobject obj, jstring content) {
	const jbyte* str=(const jbyte*)(*env)->GetStringUTFChars(env,content,JNI_FALSE);
	printf("Hello------->%s\n",str);
	(*env)->ReleaseStringUTFChars(env,content,(const char*)str);
	return;
}
6.生成os文件(Linux为os,Window为dll)

通过linux带有gcc,进行对C代码的编译。看到第5点中,需要引入jni.h等的头文件,需要 -I

gcc -fPIC -D_REENTRANT -I/usr/jdk/include/linux/ -I/usr/jdk/include -I/home/aguicode/programmer/java_project/ -c TestJNI.c
gcc -shared TestJNI.o -o libTestJNI.so     # 两个文件

生成so文件

7. 调用os,实现JNI

1)TestJNI.java中直接调用so,可以之间编译TestJNI就行,或者重新创建一个Test.java,调用TestJNI(),进而调用TestJNI.so;
2)编译生成class文件
3)运行:java TestJNI TestJNI:类名

javac TestJNI.java
java TestJNI

编译Java文件
看到hello,success,那么本次demo就完成了,感兴趣的可以试一试。

小结

大概过程是:先有TestJNI.java(1)文件,接着通过javah生成TestJNI.h(3)供C来实现TestJNI.c(4),接着系统编译so文件产生TestJNI.o(5)与TestJNI.so(5),接着通过main函数来调用native方法,java编译class文件TestJNI.class(2)
参考所需文件与生成文件

一些实战技巧

关于多cpp打包问题

以上只是针对一个头文件的实现,但是如果要实现一个企业级的JNI,那么一个so的动态链接库可能会对应很多代码实现,这里就可能不止是一个.h 与一个.cpp的实现了,可能包含有很多cpp实现,这里就需要使用makefile 来实现动态链接库的生成。
makefile
TODO:makefile企业级的使用,JNA,JNR使用~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值