JNI 学习笔记(一)-- jni函数调用流程,JNI理解和基本
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我的CSDN地址:http://blog.csdn.net/urrjdg
本文CSDN地址:http://blog.csdn.net/urrjdg/article/details/78153147
CSDN 和 简书 同步更新
看目录去CSDN
1.介绍 - JNI/NDK/静态库/动态库
1.JNI
java native interface
Java中定义的一种用于连接Java和C/C++接口的一种实现方式
使用环境:
java api 不能满足我们程序的需要的时候。
算法计算,图像渲染 效率要求非常高,
当需要访问一些已有的本地库
2.NDK
Native Development Kit
工具的集合。帮助开放者快速开放C/C++ 动态库的工具。
是Google在Android开发中提供的一套用于快速创建native工程的一个工具。使用这个工具可以很方便的编写,调试JNI的代码。
3.静态库和动态库
都是函数库。
静态库:.a (静态库在程序编译的时候就会直接连接到目标代码里面,所以在运行的时候就不需要了 )
动态库: .dll/.so (动态库在编译的时候不会自动连接到目标代码里面,就是说 这个动态库是独立吧,不会随着程序的编译直接链接进去,而是在程序运行的时候动态加载的,例如一下代码)
// 只有在运行工程的时候才会去走 这个static的代码块,这就是动态的过程
static{
System.loadLibrary("JNI_Native");
}
好处:功能独立,作为方案提供商不需要提供源码(保密)
4.JNIEnv 是什么?
C:
JNIEnv 结构体指针的别名
env 是二级指针
C++
JNIEnv 是结构体的别名
env 是一级指针
每个native 函数,都至少有两个参数(JNIEnv * , jclass/jobject)
jclass: native 静态方法
jobject: native 非静态方法
5. 数据类型
Jni基本数据类型
引用类型
String jstring在·
Object jobject
基本数据类型数组:
//type[] jTypeArray;
byte[] jByteArray;
引用类型数组
6. 属性签名
属性签名例如:Java方法:
long f(int n,String s,int[] arr);
具有以下类型签名:
(ILjava/lang/String;[I)J
2. 使用jni 进行 java 调用 C 的 静态 和非静态navtive方法
1. 新建一个 Java工程
public class JniTest01 {
// 静态方法
public native static String getStringFromC();
// 非静态方法
public native String getStringFromC2();
public static void main(String[] args) {
System.out.println("Test01");
}
}
2. 使用 javah 命令
使用 cmd 跳转到 JniTest01这个类 的当前 目录下 ,直接敲 javah ,生成 JniTest01.h 文件( 或者自己手写:java_类的全名_方法名)
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class JniTest01 */
#ifndef _Included_JniTest01
#define _Included_JniTest01
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: JniTest01
* Method: getStringFromC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_JniTest01_getStringFromC
(JNIEnv *, jclass);
/*
* Class: JniTest01
* Method: getStringFromC2
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_JniTest01_getStringFromC2
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
3. 复制.h 头文件到cpp 工程 (Vistual Studio)
复制 .h 文件的 时候要先拷贝到 工程所在的头文件夹下面, 然后在 visual studio的工程那边 : 头文件右键 添加--》 现有项
4. 复制jni.h 和jni_md.h
同上
jni.h
和jni_md.h
在
C:\develop\Java\jdk1.8.0_111\include
C:\develop\Java\jdk1.8.0_111\include\win32
记得修改 JniMain.h
中#include <jni.h>
为#include "jni.h"
5. 实现.h 头文件中的声明函数
这是个C文件 不是Cpp,先用C进行演示,后面的系列会用到C++
#include "JniTest01.h"
#include "stdafx.h"
/*
* Class: JniTest01
* Method: getStringFromC
* Signature: ()Ljava/lang/String;
*/
// 静态方法 jclass
JNIEXPORT jstring JNICALL Java_JniTest01_getStringFromC
(JNIEnv * env, jclass jclz){
return(*env)->NewStringUTF(env,"String frome C 01,ZekingLee");
}
/*
* Class: JniTest01
* Method: getStringFromC2
* Signature: ()Ljava/lang/String;
*/
// 非静态方法 jobject
JNIEXPORT jstring JNICALL Java_JniTest01_getStringFromC2
(JNIEnv * env, jobject jclz){
return(*env)->NewStringUTF(env, "String frome C 02,ZekingLee");
}
6. 生成一个dll 动态库
右键 visual studio 的项目名 --》 属性 --》 配置属性 --》 常规 --》 配置类型 --》 动态库(.dll)
平台使用x64
如果出现 : 错误 1 error C1083: 无法打开预编译头文件: “x64\Debug\ConsoleApplication1.pch”: No such file or directory C:\Users\Zeking\Desktop\JNICode\Jni01\ConsoleApplication1\ConsoleApplication1\JniTest01.c 2 1 ConsoleApplication1
右键工程名字-->属性-->配置属性-->C/C++ -->预编译头--> 选择不适用预编译头
7. 在java中加载动态库
将dll
文件 复制到 java工程里面
8. 触发native函数
public class JniTest01 {
// 静态方法
public native static String getStringFromC();
// 非静态方法
public native String getStringFromC2();
public static void main(String[] args) {
System.out.println(getStringFromC()); // 输出 String frome C 01,ZekingLee
JniTest01 jinTest01 = new JniTest01();
System.out.println(jinTest01.getStringFromC2()); // 输出 String frome C 02,ZekingLee
}
static{
System.loadLibrary("ConsoleApplication1");
}
}
3. 使用jni 进行C 调用 java 的 静态 和非静态变量
遇到这个错误的解决方法:
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. C:\Users\Zeking\Desktop\JNICode\Jni01\ConsoleApplication1\ConsoleApplication1\JniTest01.c 51 1 ConsoleApplication1
项目名右键--> 属性-->C/C++-->预处理器-->预处理定义 添加 _CRT_SECURE_NO_WARNINGS
public class JniTest02 {
// 非静态变量
public String key = "Key";
// 静态变量
public static int count = 99;
// 调用非静态变量
public native String accessField();
// 调用静态变量
public native void accessStaticField();
static{
System.loadLibrary("ConsoleApplication1");
}
public static void main(String[] args) {
JniTest02 jniTest02 = new JniTest02();
System.out.println(jniTest02.key); // Key
jniTest02.accessField();
System.out.println(jniTest02.key); // ZekingKey
System.out.println(count); // 99
jniTest02.accessStaticField();
System.out.println(count); // 100
}
}
#include "JniTest02.h"
#include "stdafx.h"
#include <string.h>
#include <stdio.h>
/*
* Class: JniTest02
* Method: accessField
* Signature: ()Ljava/lang/String;
*
*/
// 访问非静态域
JNIEXPORT jstring JNICALL Java_JniTest02_accessField
(JNIEnv * env, jobject jobj){
// jclass
jclass jclz = (*env)->GetObjectClass(env, jobj);
// fieldId 属性名称,属性签名
jfieldID fid = (*env)->GetFieldID(env, jclz, "key", "Ljava/lang/String;");
// key -> dongNao key
// 得到key 对应的值
// GetXXXField
jstring jstr = (*env)->GetObjectField(env, jobj, fid);
// jni -> c
char * c_str = (*env)->GetStringUTFChars(env, jstr, NULL);
char text[30] = "Zeking";
// 生成新的字符串 ZekingKey
strcat(text, c_str);
// C -> jni
jstring new_str = (*env)->NewStringUTF(env, text);
//setXXXField
(*env)->SetObjectField(env, jobj, fid, new_str);
(*env)->ReleaseStringChars(env, new_str, c_str);
return new_str;
}
/*
* Class: JniTest02
* Method: accessStaticField
* Signature: ()V
*/
// 访问静态域
JNIEXPORT void JNICALL Java_JniTest02_accessStaticField
(JNIEnv * env, jobject jobj){
jclass jclz = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetStaticFieldID(env, jclz, "count", "I");
if (fid == NULL){
printf("fid is Null");
}
jint count = (*env)->GetStaticIntField(env, jclz, fid);
count++;
(*env)->SetStaticIntField(env, jclz, fid, count);
}