JNI 学习笔记(一)-- jni函数调用流程,JNI理解和基本

2017-10-03  本文已影响157人  ZekingLee

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我的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;

引用类型数组

Object jobjectArray;

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



}
上一篇下一篇

猜你喜欢

热点阅读