由于c语言更接近底层,运行效率要比 Java快,所以Java开发当中会用到c语言开发一些核心库,一方面可以提高运行效率,另一方面可以让代码保密性更好,不易于反编译。本节所讲内容算是jni的基础入门,使用代码示例了Java和c之间最基本的交互。
Java代码调用示例:
C代码示例:
1.Java调用c函数
步骤:1.编写本地native方法
2.使用Javah命令生成头文件(.h文件)
3.使用c语言实现头文件中的方法
4.生成dell动态库文件
5.给dell库文件配置环境变量或者直接把dell文件直接拷到项目根目录
6.Java加载dell文件
//加载动态库
static{
System.loadLibrary("dell文件名");
}
生成的头文件代码示例:
#include "jni.h"
JNIEXPORT jstring JNICALL Java_JniExamp_JniTest_getStringFromC
(JNIEnv *, jclass);
说明:生成的头文件中引入的头文件 jni.h是jdk自带的,而且 jni.h中还引入了
jni_md.h,也是jdk中自带的.h文件
他们都属于Java和c交互的api,在实现c函数的时候,这两个文件都得引入。
实现c函数代码示例:
//该方法实现给Java返回一个字符串
JNIEXPORT jstring JNICALL Java_JniExamp_JniTest_getStringFromC
(JNIEnv *env, jclass jcls){
return (*env)->NewStringUTF(env,"C String,hello java");
}
参数说明:
JNIEnv 结构体指针,
env二级指针
代表Java运行环境,调用Java中的代码
每个native函数,都至少有两个参数(JNIEnv*,jclass或者jobject)
1)当native方法为静态方法时:
jclass 代表native方法所属类的class对象(JniTest.class)
2)当native方法为非静态方法时:
jobject 代表native方法所属的对象
public class JniTest {
public native static String getStringFromC();
static{
System.loadLibrary("libjnitest");
}
public static void main(String[] args) {
String stringFromC = getStringFromC();
System.out.println(stringFromC);
}
}
随着日志
C String,hello java
的输出,我们就完成了Java对c函数的调用。初级使用就是那么so easy!
2.C 访问Java中的成员和方法
这个章节可以分为两类
一类是 C访问Java中的静态成员和静态方法
另一类是C访问Java中的非静态成员和方法
在此之前我们先看一下在基本数据类型方面Java和jni的对应关系
基本数据
Java基本数据类型与JNI数据类型的映射关系
Java类型->JNI类型->C类型
boolean jboolean
byte jbyte;
char jchar;
short jshort;
int jint;
long jlong;
float jfloat;
double jdouble;
void void
引用类型(对象)
String jstring
object jobject
数组,基本数据类型的数组
byte[] jByteArray
对象数组
object[](String[]) jobjectArray
2.1 C访问Java中的非静态成员
Java代码示例:
public class JniTest {
public String javaString = "javaString";
//访问属性,返回修改之后的属性内容
public native String accessField();
static{
System.loadLibrary("libjnitest");
}
public static void main(String[] args) {
System.out.println("javaString修改前:"+test.javaString);
test.accessField();
System.out.println("javaString修改后:"+test.javaString);
}
}
JNIEXPORT jstring JNICALL Java_JniExamp_JniTest_accessField
(JNIEnv *env, jobject jobj){
//jobj是test对象,JniTest.class
//1.先获取jclass
jclass cls=(*env)->GetObjectClass(env,jobj);
//2.获取属性名 id
jfieldID fid=(*env)->GetFieldID(env,cls,"javaString","Ljava/lang/String;");
//最后一个参数是属性签名 可以通过javap命令获取属性签名
//具体做法是在命令行窗口中切换到Java项目的bin目录,运行 javap -s -p 类的全路径名即可获取属性签名
//3.获取javaString的属性值
jstring jstr=(*env)->GetObjectField(env,jobj,fid);
//对jstr做出变化
char *c_str = (*env)->GetStringUTFChars(env,jstr,JNI_FALSE);
//拼接得到新的字符串
char text[20] = "c change java";
strcat(text,c_str);
jstring new_jstr = (*env)->NewStringUTF(env, text);
(*env)->SetObjectField(env, jobj, fid, new_jstr);
printf("new_jstr:%#x\n", &new_jstr);
return new_jstr;
}
日志输出:
javaString修改前:javaString
javaString修改后:c change javajavaString
2.2 C访问Java中的静态成员
Java代码示例:
public class JniTest {
public static int num = 999;
//访问静态属性,修改属性内容
public native void accessStaticField();
static{
System.loadLibrary("libjnitest");
}
public static void main(String[] args) {
JniTest test=new JniTest();
System.out.println("num修改前:"+num);
test.accessStaticField();
System.out.println("num修改后:"+num);
}
}
C代码示例:
//访问Java中的静态成员
JNIEXPORT void JNICALL Java_JniExamp_JniTest_accessStaticField
(JNIEnv *env, jobject jobj){
jclass cls=(*env)->GetObjectClass(env,jobj);
//
jfieldID fid=(*env)->Get
StaticFieldID(env,cls,"num","I");
//
GetStatic<Type>Field 和访问非静态成员的区别
jint num=(*env)->Get
StaticIntField(env,cls,fid);
num++;
//修改
(*env)->SetStaticIntField(env,cls,fid,num);
}
日志输出:
num修改前:999
num修改后:1000
2.3C访问Java中的非静态方法
Java代码示例:
public class JniTest {
//C访问非静态方法
public native void accessMethod();
static{
System.loadLibrary("libjnitest");
}
//产生指定范围的随机数
public int getRandomInt(int max){
System.out.println("genRandomInt 被c执行了...");
return new Random().nextInt(max);
}
public static void main(String[] args) {
JniTest test=new JniTest();
test.accessMethod();//Java调用c实现的这个函数,在这个函数中C有调用了非静态方法getRandomInt
}
}
C代码示例:
//访问Java中的非静态方法
JNIEXPORT void JNICALL Java_JniExamp_JniTest_accessMethod
(JNIEnv *env, jobject jobj){
jclass cls=(*env)->GetObjectClass(env,jobj);
jmethodID mid=(*env)->GetMethodID(env,cls,"getRandomInt","(I)I");
jint randomNum=(*env)->CallIntMethod(env,jobj,mid,10);
printf("random num:%ld",randomNum);
}
日志输出:
genRandomInt 被c执行了...
random num:3
C访问Java中的静态方法:
Java代码示例:
public class JniTest {
//C访问静态方法
public native String accessStaticMethod();
static{
System.loadLibrary("libjnitest");
}
//产生UUID字符串
public static String getUUID(){
return UUID.randomUUID().toString();
}
public static void main(String[] args) {
JniTest test=new JniTest();
String uuid = test.accessStaticMethod();
//Java调用c实现的这个函数,在这个函数中C又调用了静态方法
getUUID
System.out.println("uuid::::"+uuid);
}
}
C代码示例:
//访问Java中的静态方法
JNIEXPORT jstring JNICALL Java_JniExamp_JniTest_accessStaticMethod
(JNIEnv *env, jobject jobj){
jclass cls=(*env)->GetObjectClass(env,jobj);
jmethodID mid= (*env)->Get
StaticMethodID(env,cls,"getUUID","()Ljava/lang/String;");
jstring uuid=(*env)->Call
StaticObjectMethod(env,cls,mid);
return uuid;
}
日志输出:
uuid::::4d34e97c-ae54-4377-86ea-de37468843b8
总结:
C在访问Java的成员和方法其实是有固定范式的。总结如下:
C访问java成员 步骤:
1.先获取jclass
2.获取属性名 id
3.获取属性值
这三个步骤对于静态变量和非静态变量都是一样的,只是在访问静态变量时方法名前面加了一个static(上面代码中有红色标注)
C访问Java方法 步骤:
1.先获取jclass
2.获取方法id
3.方法调用
这三个步骤对于静态方法和非静态方法也是一样的,只是在访问静态方法时方法名前面加了一个static(上面代码有红色标注)
本篇文章只是简单介绍了一下jni中Java和c互调的简单示例,
示例代码点这里