欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JNI函数调用流程,基本数据类型

程序员文章站 2022-06-06 20:32:09
...

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)

或者自己手写

  1. 复制.h 头文件到cpp 工程(cpp工程是vs创建的控制台应用程序 右键头文件->添加现有项->选择拷贝进来的.h 文件)
  2. 复制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;

引用类型数组
Object jobjectArray;

例子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中的变量签名列表:
  
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
相关标签: JNI