前言
JNI是JAVA标准平台中的一个重要功能,它弥补了JAVA的与平台无关这一重大优点的不足,在JAVA实现跨平台的同时,也能与其它语言(如C、C++)的动态库进行交互,给其它语言发挥优势的机会。
标准的java类库没有提供我们应用程序所需要的功能,通常这些功能是平台相关的(只能由其他语言编写);程序的某些部分对速度要求比较苛刻,我们选择用汇编或者c语言来实现并在java语言中调用他们;为了应用的安全性,会将一些复杂的逻辑和算法通过本地代码(C或C++)来实现,本地代码比字节码难以破解。
编写JNI
java代码
package com.bean.anotation.ownanotation;
public class JINTest {
//普通成员
private String addRess = "xian";
//静态成员
private static String name = "wellim";
static {
System.loadLibrary("libJNITest");
}
public static void loadMessage(String data){
System.out.println(data);
}
//普通方法
public void method(String data){
loadMessage(data);
loadMessage(addRess);
}
//静态方法
public static void staticMethod(String data){
loadMessage(data);
loadMessage(name);
}
//java调用jni
public static native String getJniData();
public static native void callStaticMethod(int c);
public static native void callStaticMethod(long i,String str);
public native void callInstanceMethod(int i);
public native void callInstanceMethod(String str,long i);
}
生成头文件(.h)
用javac -h <directory> xxx.java
命令生成对应jni头文件。头文件名为java
文件包路径+文件名:com_bean_anotation_ownanotation_JINTest
,内容就是java
文件中native
方法对应的C++
函数声明。此文件供用户自己写的C、C++程序来引用并实现其中的函数。注:生成的.h
文件需要includejni.h
根据放入项目中相对位置修改导入路径,具体视情况变动。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_bean_anotation_ownanotation_JINTest */
#ifndef _Included_com_bean_anotation_ownanotation_JINTest
#define _Included_com_bean_anotation_ownanotation_JINTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_bean_anotation_ownanotation_JINTest
* Method: getJniData
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_bean_anotation_ownanotation_JINTest_getJniData
(JNIEnv *, jclass);
/*
* Class: com_bean_anotation_ownanotation_JINTest
* Method: callStaticMethod
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_bean_anotation_ownanotation_JINTest_callStaticMethod__I
(JNIEnv *, jclass, jint);
/*
* Class: com_bean_anotation_ownanotation_JINTest
* Method: callStaticMethod
* Signature: (JLjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_bean_anotation_ownanotation_JINTest_callStaticMethod__JLjava_lang_String_2
(JNIEnv *, jclass, jlong, jstring);
/*
* Class: com_bean_anotation_ownanotation_JINTest
* Method: callInstanceMethod
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_bean_anotation_ownanotation_JINTest_callInstanceMethod__I
(JNIEnv *, jobject, jint);
/*
* Class: com_bean_anotation_ownanotation_JINTest
* Method: callInstanceMethod
* Signature: (Ljava/lang/String;J)V
*/
JNIEXPORT void JNICALL Java_com_bean_anotation_ownanotation_JINTest_callInstanceMethod__Ljava_lang_String_2J
(JNIEnv *, jobject, jstring, jlong);
#ifdef __cplusplus
}
#endif
#endif
编写cpp
自己编写的c、c++程序调用java方法和jni方法的具体实现。代码里的函数实现一定要和.h
文件中函数名称一致。
//
// Created by success on 2021/7/26.
//
#include "../jni/com_bean_anotation_ownanotation_JINTest.h"
#include <iostream>
#include <stdio.h>
//java调用jni方法
JNIEXPORT jstring JNICALL Java_com_bean_anotation_ownanotation_JINTest_getJniData
(JNIEnv * env, jclass jcls){
return env->NewStringUTF("c语言数据");
}
/*
* Class: com_bean_anotation_ownanotation_JINTest_callStaticMethod
* Method: callStaticMethod
* Signature: (I)V
*/
//调用静态方法 和修改成员变量
JNIEXPORT void JNICALL Java_com_bean_anotation_ownanotation_JINTest_callStaticMethod__I
(JNIEnv * env, jclass jclassess, jint i){
//找到相关的类 注意路径是带反斜杆 不是带.
jclass cls_First = env->FindClass("com/bean/anotation/ownanotation/JINTest");
if(cls_First == NULL){
return;
}
/**
方法参数:类名,方法名,方法签名
*/
//找到对应的方法 注意签名后面有冒号
jmethodID mtd_static_method = env->GetStaticMethodID(cls_First,"staticMethod","(Ljava/lang/String;)V");
if(mtd_static_method == NULL){
return;
}
//修改java中静态字段的值 获取成员变量 注意签名后面有冒号
jfieldID file_name = env->GetStaticFieldID(cls_First,"name","Ljava/lang/String;");
if(file_name == NULL){
return;
}
//定义静态字段的值
jstring name = env->NewStringUTF("西西");
//对成员变量设置值
env ->SetStaticObjectField(cls_First,file_name,name);
jstring data = env->NewStringUTF("call static method");
if(data == NULL){
return;
}
//调用java层静态方法
env ->CallStaticVoidMethod(cls_First,mtd_static_method,data);
//删除引用
env->DeleteLocalRef(cls_First);
env->DeleteLocalRef(name);
env->DeleteLocalRef(data);
}
/*
* Class: com_bean_anotation_ownanotation_JINTest
* Method: callInstanceMethod
* Signature: (I)V
*/
//调用java实例方法 和修改实例变量
JNIEXPORT void JNICALL Java_com_bean_anotation_ownanotation_JINTest_callInstanceMethod__I
(JNIEnv *env, jobject jobj, jint i){
//找到相关的类
jclass cls_First = env->FindClass("com/bean/anotation/ownanotation/JINTest");
if(cls_First == NULL){
return;
}
//找到对应的方法
jmethodID mtd_method = env->GetMethodID(cls_First,"method","(Ljava/lang/String;)V");
if(mtd_method == NULL){
return;
}
//找到对应的构造方法
jmethodID mtd_construct = env->GetMethodID(cls_First,"<init>","()V");
if(mtd_construct == NULL){
return;
}
jfieldID fld_address = env->GetFieldID(cls_First,"addRess","Ljava/lang/String;");
if(fld_address == NULL){
return;
}
jstring address = env->NewStringUTF("上海&苏州");
if(address == NULL){
return;
}
//创建相应的对象
jobject oFirst = env->NewObject(cls_First,mtd_construct,NULL);
if(oFirst == NULL){
return;
}
//注意此时的对象 应该是jobject对象 非jclass对象
env->SetObjectField(oFirst,fld_address,address);
jstring message = env->NewStringUTF("call instance method");
//调用java层实例方法 注意此时的对象 应该是jobject对象 非jclass对象
env->CallVoidMethod(oFirst,mtd_method,message);
//清除对象的引用
env->DeleteLocalRef(message);
env->DeleteLocalRef(cls_First);
env->DeleteLocalRef(oFirst);
env->DeleteLocalRef(address);
}
编译生成dll库
JNI调用的核心实现在在动态库libjvm.dll(Linux上是libjvm.so),而该动态库源码核心在jni.h
,该头文件又依赖jni_md.h
,所以需要把他们都拷贝到项目中
把dll导入到java项目依赖
调用
从结果看native
方法被调用,变量值被修改。
public static void main(String[] args) {
//FiledTest ft = new FiledTest();
JINTest nat = new JINTest();
nat.callInstanceMethod(1);
JINTest.callStaticMethod(32);
System.out.println(JINTest.getJniData());
System.out.println("shs");
}
/** call instance method
上海&苏州
call static method
西西
c语言数据
*/