title:2_jni基础

JNI的一般开发流程:(java访问native)

1
2
3
4
5
1.1定义好本地的 native 方法
1.2javah 命令生成h 头文件
1.3 拷贝 xxx.h、jni_md.h、jih 到 VS 的工程目录并添加依赖进来
1.4实现我们头文件中的 native 方法
1.5 生成 dll 动态,java 引 dl 动态库运行即可

流程细节:

// ecplise

1.定义好本地的 native 方法(NdkSimple.java)

1
2
3
4
5
6
7
8
9
package com.tyl.myStudy;
public class NdkSimple {
public static void main(String[] args) {
NdkSimple ndkSimple = new NdkSimple();
String signaturePassword = ndkSimple.getSingnaturePassword();
System.out.println("秘钥 = "+signaturePassword);
}
public static native String getSingnaturePassword();
}

2.cmd中cd到本文件的路径下执行javah,查看支持的命令;

1
2
3
4
5
6
错误:'javah' 不是内部或外部命令
原因:jdk版本太新了,在jdk10及以上的版本中,已经删除了javah,使用"javac -h"命令替代"javah"
解决方案:javac -h ./ NdkSimple.java
(错误: 编码 GBK 的不可映射字符 (0x9B)
原因:不能有中文哪怕是注释,添加-encoding UTF-8
解决方案:javac -h ./ NdkSimple.java -encoding UTF-8

3.本地路径上出现新生成的文件:com_tyl_myStudy_NdkSimple.h

// visualStudio

4.将com_tyl_myStudy_NdkSimple.h复制到visualStudio的项目根目录中(如:D:\C++Projects\C++Study\Project1)

5.本地java的安装目录中搜索jni,然后将jni.h及jni_md.h文件复制到visualStudio的项目根目录中(如:D:\C++Projects\C++Study\Project1)

6.vs中点击头文件->添加->现有项->将com_tyl_myStudy_NdkSimple.h和jni.h及jni_md.h文件添加进来;

image-20230710095206945

image-20230710095236313

7.生成dll动态库

7-1:调试–>调试属性->常规->配置类型->动态库(.dll)->应用->确定

image-20230710093341443

7-2:生成->配置管理器->平台处新建->x64或其他根据本地环境->确定

image-20230710093615611

7-3:生成->生成解决方案;提示:生成: 1 成功,0 失败,0 最新,0则成功,文件在项目根目录-x86-debug下的dll文件就是

//ecplise NdkSimple.java

8.引入加载我们的本地库

1
2
3
4
5
static{
//System.loadlibrary:android加载apk中的libs目录下.so库
// System.load:更载一个具体路径上的.so 库,可以去服务器上下载后下载
System.load("D:\\C++Projects\\C++Study\\Project1\\x64\\Debug\\Project1.dll");
}

9.执行调用

image-20230710100300639

10.com_tyl_myStudy_NdkSimple.h头文件字段说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h" // "" 引入自己工程的头文件 <> 引入系统的头文件
/* Header for class com_tyl_myStudy_NdkSimple */
// 用来打一个标记,c在编译的时候会把头文件 copy 到你引入的地方,不管是重复引用还是相互引用都只会 copy 一次
#ifndef _Included_com_tyl_myStudy_NdkSimple
#define _Included_com_tyl_myStudy_NdkSimple
#ifdef __cplusplus// 相当于 if 语句 c++(如果是c++执行下列的方法,使用c的编译方式)
// 不管是 c 还是 c++ 统一都是采用 c 的编译方式,因为在c里面是不允许函数重载的,但是在 c++ 里面可以
extern "C" {
#endif
/*
* Class: com_tyl_myStudy_NdkSimple
* Method: getSingnaturePassword
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_tyl_myStudy_NdkSimple_getSingnaturePassword
(JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

11.jni.c的字段说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//1.实现我们的 native 方法
#include "com_tyl_myStudy_NdkSimple.h"
//2.将.h中的方法复制过来修改为实现方法
// JNIEXPORT JNI 一个关键字,不能少(编译能通过),标记为该方法可以被外部调用
// jstring : 代表 java 中的 String
// JNICALL: 也是一个关键字,可以少的 jni call
// JNIEnv: 这个是 c 和 java 相互调用的桥梁,所有 function 搞清
// jobject: java传递下来的对象,就是本项目中 JniSimple java 对象
// jclass: java传递下来的 class 对象,就是本项目中的 JniSimple.class
JNIEXPORT jstring JNICALL Java_com_tyl_myStudy_NdkSimple_getSingnaturePassword
(JNIEnv* env, jclass jobj) {//JNIEnv*, jclass
// JNIEnv * 其实已经是一个二级指针了,所以 -> 调用的情况下必须是一级指针 *取值
return (*env)->NewStringUTF(env,"5555");
}

12.native修改java参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
JNIEXPORT void JNICALL Java_com_darren_ndk12_NdkSimple1_changeName
(JNIEnv *env, jobject jobj){
// 获取 name 属性然后修改为 Jack
// 3.获取 jclass
jclass j_clz = (*env)->GetObjectClass(env, jobj);
// 获取 jfieldId (JNIEnv *env, jclass clazz, const char *name, const char *sig)
// name 获取哪个属性的属性名
// 2.sig 属性的签名
jfieldID j_fid = (*env)->GetFieldID(env, j_clz, "name", "Ljava/lang/String;");
// 1.获取 name 属性的值
jstring j_str = (*env)->GetObjectField(env, jobj, j_fid);
// 打印字符串 jstring -> c_str
char* c_str = (*env)->GetStringUTFChars(env,j_str,NULL);
printf("name is %s",c_str);
// 修改成 jack
jstring jackName = (*env)->NewStringUTF(env,"Jack");
(*env)->SetObjectField(env, jobj, j_fid, jackName);
}

13.native调用java方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//java:
public int add(int numberl,int number2){
return numberl+number2;
}

//native:
JNIEXPORT void JNICALL Java_com_darren_ndk12_NdkSimple1_callAddMathod
(JNIEnv *env, jobject jobj){
jclass j_clz = (*env)->GetObjectClass(env,jobj);
// 获取 methodid
jmethodID j_mid = (*env)->GetMethodID(env, j_clz, "add", "(II)I");
// 去调用 java 的方法
jint sum = (*env)->CallIntMethod(env, jobj, j_mid,2,3);
printf("sum = %d",sum);
}