JNI语法之数据类型和C访问Java属性和方法

本文详细介绍Java Native Interface (JNI) 的使用方法,包括如何调用本地方法、访问Java字段及方法等。并通过具体示例展示了如何从C/C++代码访问Java对象的属性与方法。

Java属性与方法签名列表:


java的方法签名可以用javap -s命令来拿到,进入到java项目的bin目录然后执行命令即可,如图所示:


java文件:

package com.tz.jni;

import java.util.Random;
import java.util.UUID;

public class Test {
	
	public String name = "Dream";
	
	public static int age = 18;
	
	//获取字符串
	public native String getTextString();
	//更新属性值
	public native String updateName();
	//更新年龄
	public native void updateAge();
	
	public native int getNumber();
	
	public native void executeRandomUUID();
	
	public static void main(String[] args) {
		
		Test test = new Test();
		System.out.println(test.getTextString());
		//一旦调用了该方法,在C中更新name
		test.updateName();
		System.out.println("更新后的值:"+test.name);
		test.updateAge();
		System.out.println("更新后的age值:"+age);
		
		System.out.println("得到一个数:"+test.getNumber());
		
		test.executeRandomUUID();
	}
	
	/**
	 * 生成一个随机数
	 * @param max
	 * @return
	 */
	public int getRandomInt(int max){
		Random rd = new Random();
		return rd.nextInt(max);
	}
	
	/**
	 * 生成一个随机字符串
	 * @return
	 */
	public static String getUUID(){
		return UUID.randomUUID().toString();
	}
	
	//加载库
	static{
		//加载动态库
		//补充一下:
		//Windows系统下:.dll
		//Linux环境下:.so(Android)
		//今天加载dll动态库
		System.loadLibrary("NDK_JNI_JAVA_TEST");
	}

}



头文件:

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

#ifndef _Included_com_tz_jni_Test
#define _Included_com_tz_jni_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_tz_jni_Test
 * Method:    getTextString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_tz_jni_Test_getTextString
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

c文件:

#define _CRT_SECURE_NO_WARNINGS
//注意:导入系统的头文件<>,导入自己头文件""
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "com_tz_jni_Test.h"

//内容一:在windows下,通过JNI调用dll动态库的实现
//第一步:定义native方法
//第二步:通过javah命令生成.h头文件
//第三步:将生成.h头文件复制到C项目当中(vs)
//第四步:导入jni支持头文件(将jni.h、jni_md.h复制到C项目中,以上两个文件在jdk安装目录下)
//第五步:实现native方法(实际上实在c中实现)
//第六步:配置属性,生成dll动态库
//第七步:配置dll动态库所在的目录(配置环境变量:目的为了在java中能够调用到)
//第八步:重启eclipse(目的:为了让eclipse重新读取系统环境变量配置)
//第九步:启动运行

//补充:如果你不想这么麻烦每一次都去配置环境变量,那么你就固定配置一个环境变量目录
//之后的这些dll动态库,拷贝下去就好了

//内容二:分析JNI方法

//原始写法(一般写法)
//jstring Java_com_tz_jni_Test_getTextString
//(JNIEnv *env, jobject jobj){
//	return (*env)->NewStringUTF(env, "Hello Jni!");
//}
//1.1 分析JNIEXPORT:宏定义(作用:dll动态库中允许该函数被外部调用)
//为什么要这些?行业规范(编译动态库规范)--固定格式
//1.2 分析JNICALL:用于约束函数入栈顺序和堆栈清理规则(注意:windows下的规则__stdcall)
//如果删除JNIEXPORT和JNICALL也不会报错,系统编译时候自动加上
//1.3 分析方法名称含义:Java_com_tz_jni_Test_getTextString
//一种命名规范(区分方法)(说白了就是唯一标识)
//1.4 分析JNIEnv是什么?干了什么事情?
//JNIEnv是一个结构体指针(指针也是变量,也可以存储指针地址)
//目的:通过JNIEnv访问Java中的方法,属性,创建对象,加载类等等......
//1.5 分析jobject(代表了什么?什么含义)
//两张情况(jobject有两张含义)
//第一种情况:如果你的native方法不是静态方法,那么jobject代表该方法对应的java对象
//(例如:说白了就是我们java中调用的test对象)
//第二种情况:如果你的native方法是静态方法,那么jobject代表该方法对应的class对象
JNIEXPORT jstring JNICALL Java_com_tz_jni_Test_getTextString
(JNIEnv *env, jobject jobj){
	return (*env)->NewStringUTF(env, "Hello Jni!");
}

//jni中基本数据类型
//java      jni(中介)    C
//boolean   jboolean   char
//int       jint        int
//double    jdouble    double
//以此类推基本数据类型对应关系

//引用类型
//java      jni(中介)    C
//String    jstring   char字符数组
//Object    jobject     ....
//注意:基本数据类型数组也是引用类型
//byte[]    jbyteArray  ....


//C访问Java成员(访问实例属性)
JNIEXPORT jstring JNICALL Java_com_tz_jni_Test_updateName
(JNIEnv *env, jobject jobj){
	//在C中不能够通过实例调用属性(说白了就是反射)
	//class对象(类对象)
	jclass cls = (*env)->GetObjectClass(env, jobj);
	//获取属性(java中称之为属性对象)
	//参数一:JNIEnv *env
	//参数二:class对象
	//参数三:属性名称
	//参数四:属性签名(说白了就是去指定类型)
	//属性签名就是类型的简写
	jfieldID fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
	//获取属性的值(类似java反射中)
	//注意:Get<签名类型>Field
	//参数一:JNIEnv *env
	//参数二:哪一个对象--jobj
	//参数三:哪一个属性--fid
	//返回值:就是我们的jstring(在java中所有的类都是object子类)
	//在jni中也是,所有的都是jobject的子类
	jstring jstr = (*env)->GetObjectField(env, jobj, fid);

	//将jni的类型转成C/C++中的数据类型
	char* str = (*env)->GetStringUTFChars(env, jstr, NULL);

	//更新str
	char newStr[] = "Jackey";
	strcat(newStr, str);

	//又要将C/C++数据类型转成jni类型
	jstr = (*env)->NewStringUTF(env,newStr);

	//更新java对象中的属性的值
	(*env)->SetObjectField(env, jobj, fid, jstr);

	return jstr;
}

//C/C++访问Java中的静态属性
JNIEXPORT void JNICALL Java_com_tz_jni_Test_updateAge
(JNIEnv *env, jobject jobj){
	//在C中不能够通过实例调用属性(说白了就是反射)
	//class对象(类对象)
	jclass cls = (*env)->GetObjectClass(env, jobj);
	//获取属性
	jfieldID fid = (*env)->GetStaticFieldID(env, cls, "age", "I");
	//获取值
	//注意:GetStatic<类型>Field
	jint age = (*env)->GetStaticIntField(env, cls, fid);
	//jint就是long类型,可以直接操作
	age += 50;
	//更新java静态属性值
	//注意:SetStatic<类型>Field
	(*env)->SetStaticIntField(env, cls, fid, age);
}

//C/C++访问java实例方法(学习如何调用)
//小案例:生成随机数
JNIEXPORT jint JNICALL Java_com_tz_jni_Test_getNumber
(JNIEnv *env, jobject jobj){
	//在C中不能够通过实例调用属性(说白了就是反射)
	//class对象(类对象)
	jclass cls = (*env)->GetObjectClass(env, jobj);

	//获取实例方法
	//javap -s -p 类名
	jmethodID mid = (*env)->GetMethodID(env, cls, "getRandomInt","(I)I");
	//调用方法(执行方法)类似于java中的invoke
	//参数一:env
	//参数二:实例对象
	//参数三:调用哪一个方法
	//参数四:传入的参数列表
	jint randomInt = (*env)->CallIntMethod(env, jobj, mid, 100);


	return randomInt;
}


//C/C++访问java静态方法
//javap命令,能够获取签名
JNIEXPORT void JNICALL Java_com_tz_jni_Test_executeRandomUUID
(JNIEnv *env, jobject jobj){
	//在C中不能够通过实例调用属性(说白了就是反射)
	//class对象(类对象) Class<?> clz
	jclass cls = (*env)->GetObjectClass(env, jobj);
	//获取静态方法
	jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
	//执行方法
	jstring jstr = (*env)->CallStaticObjectMethod(env, cls, mid);
	
	//将jstring转成c的字符串
	char* uuid = (*env)->GetStringUTFChars(env, jstr, NULL);
	//可以将值写到一个文件中
	char path[100];
	sprintf(path, "F://%s.txt", uuid);

	//将uuid写入文件中
	FILE*f = fopen(path, "w");
	fputs(uuid, f);
	fclose(f);
}



整理自示例代码




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值