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

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

作者: ZekingLee | 来源:发表于2017-10-03 01:41 被阅读157次

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

    相关文章

      网友评论

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

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