美文网首页
JNI基础笔记-env原理

JNI基础笔记-env原理

作者: 月影路西法 | 来源:发表于2019-07-09 21:21 被阅读0次

1. JNI 的一般开发流程

 1.1 定义好本地的 native 方法

 1.2 javah 命令生成 .h 头文件

1.3 拷贝 xxx.h、jni_md.h、jni.h 到 VS 的工程目录并添加依赖进来

1.4 实现我们头文件中的 native 方法

1.5 生成 dll 动态,java 引入 dll 动态库运行即可

2.头文件的基础

C语言中#ifdef,#ifndef和#endif的作用

  先说结论,这种用法的目的是为了防止重复定义,而不是所谓的重复声明。

   其实这一点也是很容易理解的,头文件的定义本身就是为了引入“声明”的,如果不允许重复声明,那多个文件引用同一个头文件就非法了,这显然是错误的,在C编译中,恰恰是允许也需要重复声明的,只要引用声明了,就可以远程使用该声明对应的对象(变量或函数)。

a.h

#include <stdio.h>

#include "b.h"

b.h

#include "a.h"

c.c

#include "a.h"

#include "b.h"

int main(){

    printf("Hello!");

}

如果你程序是这样写的话,编译器就会出现Error #include nested too deeply的错误。

因为这里 b.h 和 a.h 都互相包含,c.c文件在include的时候重复include了a.h,我们希望c.c文件中执行#include "b.h"的时候 b.h 能进行判断,如果没有#include "a.h"则include,如果已经include了,则不再重复定义。

可以将b.h修改为:

#ifndef _A_H

#define _A_H

#include "a.h"

#endif

   这里,在头文件中出现了变量定义,这种情况下,这个#ifndef才有了作用,第一次被引用时,该定义能够同时被包含,但是第二次,由于已经define xxx了,所以这个定义就不能被重复包含了,这样就能有效的避免重复定义而报错了,因为编译过程中,声明可以,但是重复定义是不可以的。

3. JNIEnv 的实现原理

typedef const structJNINativeInterface_*JNIEnv;

JNIEnv是什么?结构体指针的别名  JNIEnv 其实就是 JNINativeInterface_的指针别名,其实就是一个一级指针了

实现我们的 native 方法

#include <stdlib.h>

#include <stdio.h>

// 定义一个结构体指针的别名

typedef const struct JNINativeInterface *JNIEnv;

// 模拟一个结构体

struct JNINativeInterface{

// 结构体的方法指针

char*(*NewStringUTF)(JNIEnv*,char*);

};

char* NewStringUTF(JNIEnv* env, char* c_str){

// c_str -> jstring

return c_str;

}

char* Java_com_darren_getSingnaturePassword(JNIEnv * env){

// JNIEnv * 其实已经是一个二级指针了,所以 -> 调用的情况下必须是一级指针 *取值

return (*env)->NewStringUTF(env, "940223");

}

void main(){

// 构建 JNIEnv* 对象

struct JNINativeInterface nativeInterface;

// 给结构方法指针进行复制(实现)

nativeInterface.NewStringUTF = NewStringUTF;

// 传给 Java_com_darren_ndk12_NdkSimple_getSingnaturePassword 的参数是 JNIEnv*

JNIEnv env = &nativeInterface;// 一级指针

JNIEnv* jniEnv = &env;// 二级指针

// 把 jniEnv 对象传给 Java_com_darren_ndk12_NdkSimple_getSingnaturePassword

char* jstring = Java_com_darren_getSingnaturePassword(jniEnv);

// jstring 通过 JNIEnv 传给 java 层

printf("jstring = %s",jstring);

getchar();

}

#include "com_darren_ndk12_NdkSimple.h"

#include "com_darren_ndk12_NdkSimple1.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_darren_ndk12_NdkSimple_getSingnaturePassword

(JNIEnv * env, jobject jobj){

// JNIEnv * 其实已经是一个二级指针了,所以 -> 调用的情况下必须是一级指针 *取值

return (*env)->NewStringUTF(env,"940223");

}

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);

}

JNIEXPORT void JNICALL Java_com_darren_ndk12_NdkSimple1_changeAge

(JNIEnv * env, jclass jcls){

// 首先获取原来的

jfieldID j_fid = (*env)->GetStaticFieldID(env,jcls,"age","I");

// Static 获取静态的

jint age = (*env)->GetStaticIntField(env, jcls, j_fid);

// jint -> int

age += 12;

// 设置新的 age 参数

(*env)->SetStaticIntField(env,jcls,j_fid,age);

}

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);

}

public class NdkSimple1 {

public String name = "Darren"; // name = Jack

public static int age = 24;// 24 + 12

public int add(int number1,int number2){

return number1+number2;

}

// 小的思考:静态获取 uuid 的方法,然后再 c 调用这个方法获取uuid

public static String getUUID(){

return UUID.randomUUID().toString();

}

public static void main(String[] args) {

NdkSimple1 ndSimple1 = new NdkSimple1();

/*System.out.println("修改前:"+ndSimple1.name);

ndSimple1.changeName();

System.out.println("修改后:"+ndSimple1.name);*/

/*System.out.println("修改前:"+NdkSimple1.age);

changeAge();

System.out.println("修改后:"+NdkSimple1.age);*/

ndSimple1.callAddMathod();

}

// 改变我们属性

public native void changeName();

public static native void changeAge();

// c 调用 java 方法

public native void callAddMathod();

static{

// 引入加载我们的动态库

// System.loadLibrary :android 加载apk中的libs目录下 .so 库

// System.load : 加载一个具体路径上的 .so 库,去服务器上下载再进行加载(data/data)

System.load("C:/Users/hcDarren/Desktop/android/NDK/NDK_Day12/x64/Debug/NDK_Day12.dll");

}

}

查找方法的规则 javap -v -s 文件名称

相关文章

网友评论

      本文标题:JNI基础笔记-env原理

      本文链接:https://www.haomeiwen.com/subject/vchjoqtx.html