JNI 基础 - JNIEnv 的实现原理
2018-09-15 本文已影响3人
Peakmain
JNI的一般开发流程
1.1 定义好本地的 native 方法
public static void main(String[] args) {
NdkSimple ndkSimple=new NdkSimple();
String singnaturePassword = ndkSimple.getSingnaturePassword();
System.out.println("秘钥=="+singnaturePassword);
}
//定义好本地的native方法
public native String getSingnaturePassword();
1.2 javah 命令生成 .h 头文件
我用的是idea工具,在Terminal下首先定位到src目录下,然后在输入
javah -jni com.example.demo.NdkSimple
1.3 拷贝 xxx.h、jni_md.h、jni.h 到 VS 的工程目录并添加依赖进来
image.png
image.pngjni.h和jni_md.h到自己安装的jdk目录下搜索jni即可
1.4 实现我们头文件中的 native 方法
#include "com_example_demo_NdkSimple.h"
JNIEXPORT jstring JNICALL Java_com_example_demo_NdkSimple_getSingnaturePassword(JNIEnv * env, jobject jobj){
return (*env)->NewStringUTF(env, "9931005");
}
1.5 生成 dll 动态,java 引入 dll 动态库运行即可
代码写完后选择调试->最后一个选项xx属性,我的项目名字叫jni.
image.png
配置属性->常规->项目默认值->配置类型->选择dll选项
image.png
生成->配置项目管理器
image.png
平台->新建
image.png
我的是x64
image.png
生成->生成解决方案
image.png
java中引入动态库即可,动态库在自己的ndk项目x64目录下
image.png
static {
//引入加载我们的动态库
//System.loadLibrary():android中加载apk中的.so库
//System.load()加载一个具体路径上的so库,去服务器下载再进行加载
System.load("D:/visual studio 2013/Projects/jni/x64/Debug/jni.dll");
}
详解.h头文件和实现文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_demo_NdkSimple */
//用来打印一个标记,在c编译的时候会把头文件拷贝到你引入的地方,不管是重复引用还是相互引用都只会copy一次
#ifndef _Included_com_example_demo_NdkSimple
#define _Included_com_example_demo_NdkSimple
#ifdef __cplusplus//相当于if语句 c++
//不管是c还是c++统一都是采用c的编译方式,因为在c里面不允许函数重载的,但是c++里面是可以的
extern "C" {
#endif
/*
* Class: com_example_demo_NdkSimple
* Method: getSingnaturePassword
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_demo_NdkSimple_getSingnaturePassword
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
#include "com_example_demo_NdkSimple.h"
//JNIEXPORT JNI一个关键字,不能少。如果少了,编译能通过,但是运行出错。标记该方法可以被外部调用
//jstring代表java中string
//JNICALL 也是一个关键字,可以没有。jni call
//JNIEnv:这个是java和c相互调用的桥梁
//jobject:java传递下来的对象,就是本项目JniSimple.class对象
//jclass:java传递下来的class对象,就是本项目JniSimple.class对象
JNIEXPORT jstring JNICALL Java_com_example_demo_NdkSimple_getSingnaturePassword(JNIEnv * env, jobject jobj){
return (*env)->NewStringUTF(env, "9931005");
}
c访问java属性
首先java方法
public class NdkSimple1 {
static {
//引入加载我们的动态库
//System.loadLibrary():android中加载apk中的.so库
//System.load()加载一个具体路径上的so库,去服务器下载再进行加载
System.load("D:/visual studio 2013/Projects/jni/x64/Debug/jni.dll");
}
private String name = "peakmain";
public static int age = 18;
public static void main(String[] args) {
NdkSimple1 ndkSimple1 = new NdkSimple1();
System.out.println("alert before:" + ndkSimple1.name);
System.out.println("alert before:" + ndkSimple1.age);
ndkSimple1.changeName();
ndkSimple1.changeAge();
System.out.println("alert after:" + ndkSimple1.name);
System.out.println("alert after:" + ndkSimple1.age);
}
public native void changeName();
public static native void changeAge();
}
生成头文件,获得类的签名文件如我的项目结构是,定位到test目录下,若不记得命令输入javap即可
javap -p -s com.example.demo.NdkSimple1
image.png
我的结果图
image.png
c实现代码
JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_changeName
(JNIEnv *env, jobject jobj){
//获取name属性,修改名字为Treasure
// 3.获取 jclass
jclass j_clz = (*env)->GetObjectClass(env, jobj);
// 获取 jfieldId (JNIEnv *env, jclass clazz, const char *name, const char *sig)
// name 获取哪个属性的属性名
// 2.sig 属性的签名,**javap -p -s com.example.demo.NdkSimple1**
jfieldID j_fid = (*env)->GetFieldID(env, j_clz, "name", "Ljava/lang/String;");
// 1.获取 name 属性的值
jstring j_str = (*env)->GetObjectField(env, jobj, j_fid);
//打印字符串
char* c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
printf("name is %s", c_str);
//修改名字为Treasure
jstring TreaureName = (*env)->NewStringUTF(env, "Treasure");
(*env)->SetObjectField(env, jobj, j_fid, TreaureName);
}
JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_changeAge
(JNIEnv *env, jclass jclass){
// 首先获取原来的
jfieldID j_fid = (*env)->GetStaticFieldID(env, jclass, "age", "I");
// Static 获取静态的
jint age=(*env)->GetStaticIntField(env, jclass, j_fid);
// jint -> int
age += 6;
// 设置新的 age 参数
(*env)->SetStaticIntField(env, jclass, j_fid, age);
}
生成动态库即可
c访问java方法
添加方法
public native void callAddMethod();
public int add(int number1, int number2) {
return number1 + number2;
}
c实现代码
- 一般方法
JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_callAddMethod
(JNIEnv *env, jobject jobj){
jclass jclass=(*env)->GetObjectClass(env, jobj);
//获取method
jmethodID j_mid = (*env)->GetMethodID(env, jclass,"add" , "(II)I");
//去调用java的方法
jint sum = (*env)->CallIntMethod(env, jobj, j_mid, 2, 3);
printf("sum = %d", sum);
}
- 静态常量方法
java方法
public static String getUUID() {
return UUID.randomUUID().toString();
}
public static native void callGetUUID();
c实现代码
JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_callGetUUID
(JNIEnv *env, jclass jclz){
// 2.获取 methodId
jmethodID j_mid = (*env)->GetStaticMethodID(env, jclz, "getUUID", "()Ljava/lang/String;");
// 1. 调用 getUUID 的方法 static
jstring j_uuid = (*env)->CallStaticObjectMethod(env, jclz, j_mid);
//jstring->c_str
char* c_uuid=(*env)->GetStringUTFChars(env, j_uuid, NULL);
// 回收,字符串回收
//(*env)->ReleaseStringUTFChars(env, j_uuid, c_uuid);
printf("c_uuid = %s", c_uuid);
}