JNI函数调用流程,基本数据类型
JNI
java native interface
什么时候使用:
java api 不能满足我们程序的需要的时候。
算法计算,图像渲染 效率要求非常高,
当需要访问一些已有的本地库
NDK
工具的集合。帮助开放者快速开放C/C++ 动态库的工具。
JNI 开发
c 用visual studio , java 用 eclipse
例子01 静态方法 jni基本使用
java 调用c 返回字符串
步骤:
1. 编写native 方法( public native static String getStringFromC(); )
2. javah 命令,生成.h 文件
java_类的全名_方法名(例如下面的例子 cmd 下 javah JniMain)
或者自己手写
- 复制.h 头文件到cpp 工程(cpp工程是vs创建的控制台应用程序 右键头文件->添加现有项->选择拷贝进来的.h 文件)
- 复制jni.h 和jni_md.h(在jdk里面 搜索jni 即可 把
public class JniMain {
public native static String getStringFromC();
static {
System.loadLibrary("myJniOne");
}
public static void main(String[] args){
System.out.println(getStringFromC());
}
}
c 里面
如果是c程序,要用 (*env)->
如果是C++要用 env->
==ps:在linux下如果.c文件中用 “env->” 编译会找不到此结构,必须用“(*env)->”,或者改成.cpp文件,以 c++的方式来编译。==
为什么: 因为c语言和c++ 需要通用头文件. c语言用二级指针 C++ 用一级指针 为了兼容头文件.
#include "stdafx.h"
//stdafx.h 预处理命令
#include "JniMain.h"
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC
(JNIEnv* env, jclass jclz){
//return (*env)->NewStringUTF(env,"C String");
//定义一个C语言字符串
char* cstr = "hello form c";
//返回值是java字符串,所以要将C语言的字符串转换成java的字符串
//在jni.h 中定义了字符串转换函数的函数指针
jstring jstr2 = (*env)->NewStringUTF(env, cstr);
return jstr2;
}
打印结果:
hello form c
- 静态库和动态库
都是函数库。 - 静态库:.a 编译的时候就链接到目标代码里面去
- 动态库: .dll/.so 不会自动连接到连接到目标代码里面去,而是在运行的时候动态加载的.
例如:
static {
System.loadLibrary("myJniOne");
}
例子02 非静态方法.
java 调用c 返回字符串 非静态方法
java代码
public class JniMain {
public native static String getStringFromC();
public native String getStringFromC2();
static {
System.loadLibrary("myJniOne");
}
public static void main(String[] args){
//静态方法
System.out.println(getStringFromC());
//非静态方法
JniMain jniMain = new JniMain();
System.out.println(jniMain.getStringFromC2());
}
}
c 代码
#include "stdafx.h"
//stdafx.h 预处理命令
#include "JniMain.h"
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC
(JNIEnv* env, jclass jclz){
//定义一个C语言字符串
char* cstr = "hello form c";
//返回值是java字符串,所以要将C语言的字符串转换成java的字符串
//在jni.h 中定义了字符串转换函数的函数指针
jstring jstr2 = (*env)->NewStringUTF(env, cstr);
return jstr2;
}
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC2
(JNIEnv * env, jobject jobj){
//定义一个C语言字符串
char* cstr = "hello form c2";
//返回值是java字符串,所以要将C语言的字符串转换成java的字符串
//在jni.h 中定义了字符串转换函数的函数指针
jstring jstr2 = (*env)->NewStringUTF(env, cstr);
return jstr2;
}
打印结果:
hello form c
hello form c2
可以看下c代码 静态方法和非静态方法有什么区别:
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC
(JNIEnv* env, jclass jclz)
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC2
(JNIEnv * env, jobject jobj)
第二个参数 jclass 和 jobject , jclass 代表的意义就是JniMain , jobject 代表的就是调用的对象JniMain .
JNIEnv 是什么?
C:
JNIEnv 结构体指针的别名
env 是二级指针
C++
JNIEnv 是机构体的别名
env 是一级指针
每个native 函数,都至少有两个参数(JNIEnv * , jclass/jobject)
jclass: native 静态方法
jobject: native 非静态方法
JNI基本数据类型:
java —— JNI —— C
boolean jboolean;
byte jbyte;
char jchar;
short jshort;
int jint;
long jlong;
float jfloat;
double jdouble;
引用类型:
String jstring;
Object jobject;
基本数据类型数组:
//type[] jTypeArray;
type 可以是int string等.
byte[] jByteArray;
例子03 访问非静态域
作用: c 修改 java 变量.
过程: 动态的过程 java程序 -> jni -> 转化成c语言可以识别的类型-> 利用c语言进行修改-> 修改完了之后返回jni
//访问非静态域
JNIEXPORT jstring JNICALL Java_JniMain_accessField
(JNIEnv * env, jobject jobj){
//得到jclass
jclass jclz = (*env)->GetObjectClass(env, jobj);
/*反射:Class (反射的入口)
反射:Field (成员变量)
反射:Method (成员方法)*/
//fieldID 属性名称
jfieldID fid = (*env)->GetFieldID(env, jclz, "key", "Ljava/lang/String");
//key 变成 cTestKey
//得到key对应的值
//getXXXField 我这里获取的是object 当然也可以使用String
jstring jstr = (*env)->GetObjectField(env, jobj, fid);
// jni -> c
char * c_str = (*env)->GetStringUTFChars(env, jstr, NULL);
char text[30] = "TestC";
//生成 拼接 两个字符串
strcat(text, c_str);
//c->jni c 字符串转化成jni java能够识别的
jstring new_str = (*env)->NewStringUTF(env, text);
//调用set 设置到里面去
(*env)->SetObjectField(env, jobj, fid, new_str);
//ReleaseStringChars释放指向Unicode格式的char*的指针
(*env)->ReleaseStringChars(env, new_str, c_str);
return new_str;
}
(1)调用GetObjectClass方法来获取Jclass,GetObjectClass的参数就是obj
(2)调用GetFieldID方法来获取jfieldID,这里要说明一下Jni的所有操作,其实就是操作方法或者是操作属性两种。操作方法时需要根据方法的ID(jmethodID)来操作,可以理解为jmethodID标识了这个方法,也就是通过这个jmethodID可以找到你要找的方法。同理操作属性时也要根据该属性的ID(jfieldID )来操作。上面那段代码里我们要改变 变量key的值,所以要先获取该变量的jfieldID 。获取变量的jfieldID 方法是GetFieldID。GetFieldID需要3个参数。第一个是上一步获取的Jclass,第二个参数是Java中的变量名,最后一个参数是变量签名(int 的变量签名是”I“)。
下面是所有Jni中的变量签名列表:
注意: ==Ljava/lang/String;== 最后有个分号
还有个问题 生成解决方案的时候可能会出现这个错误
错误 2 error C4996: ‘strcat’: This function or variable may be unsafe. Consider using strcat_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. E:\myC\JNI\ndk_6\myJniOne\myJniOne\01.c 51 1 myJniOne解决方案:
右键项目->属性->c/c++->预处理器->预处理定义->编辑->添加 _CRT_SECURE_NO_WARNINGS
java 代码:
public class JniMain {
public native static String getStringFromC();
public native String getStringFromC2();
private String key = "key";
public native String accessField();
static {
System.loadLibrary("myJniOne");
}
public static void main(String[] args){
//静态方法
System.out.println(getStringFromC());
//非静态方法
JniMain jniMain = new JniMain();
System.out.println(jniMain.getStringFromC2());
//访问非静态域 因为key 不是static 所以用对象的
System.out.println("改变之前的key ="+jniMain.key);
jniMain.accessField();//调用改变
System.out.println("改变之后的key ="+jniMain.key);
}
}
打印结果:
hello form c
hello form c2
改变之前的key =key
改变之后的key =TestCkey
做到了调用jni 改变java 的非静态变量.
例子04 访问静态域
和上面类似 不具体说了
c语言代码
//访问静态域
JNIEXPORT jstring JNICALL Java_JniMain_accessStaticField
(JNIEnv * env, jobject jobj){
//得到jclass
jclass jclz = (*env)->GetObjectClass(env, jobj);
//fieldId 属性名称, 属性签名
jfieldID fid = (*env)->GetStaticFieldID(env, jclz, "count","I");
jint count = (*env)->GetStaticIntField(env, jclz, fid);
count = count + 10;
//最后设置进去
(*env)->SetStaticIntField(env, jclz, fid, count);
}
java代码
public class JniMain {
public native static String getStringFromC();
public native String getStringFromC2();
private String key = "key";
private static int count = 6;
public native String accessField();
public native void accessStaticField();
static {
System.loadLibrary("myJniOne");
}
public static void main(String[] args){
//静态方法
System.out.println(getStringFromC());
//非静态方法
JniMain jniMain = new JniMain();
System.out.println(jniMain.getStringFromC2());
//访问非静态域 因为key 不是static 所以用对象的
System.out.println("改变之前的key ="+jniMain.key);
jniMain.accessField();//调用改变
System.out.println("改变之后的key ="+jniMain.key);
//访问静态域
System.out.println("改变前的count = "+jniMain.count);
jniMain.accessStaticField();
System.out.println("改变后的count = "+jniMain.count);
}
}
打印结果:
hello form c
hello form c2
改变之前的key =key
改变之后的key =TestCkey
改变前的count = 6
改变后的count = 16
上一篇: AC自动机+模板+hdu 3065
下一篇: java调用本地方法--jni简介
推荐阅读
-
imutils库源码解析,看他如何调用opencv基本函数
-
python 之 前端开发( JavaScript变量、数据类型、内置对象、运算符、流程控制、函数)
-
Mysql-自带的一些功能,基本用法(视图,触发器,事务,存储过程,函数,流程控制)
-
Python中函数的基本定义与调用及内置函数详解
-
Go语言中函数的参数传递与调用的基本方法
-
字符设备驱动开发 Linux 设备号 字符设备驱动开发步骤 open 函数调用流程 设备号的组成 设备号的分配 Linux 应用程序对驱动程序的调用 字符设备注册与注销 实现设备的具体操作函数
-
gcc -finstrument-functions 追踪函数调用,获取程序的执行流程
-
python六种基本数据类型及常用函数展示
-
03-基本数据类型---字符串函数
-
Python函数的基本定义和调用以及内置函数