(一)JNI 开发流程

作者: onestravel | 来源:发表于2019-03-11 10:01 被阅读7次

    JNI 开发流程

    一、C 语言执行的流程

    1. 编辑:编写代码的过程。
    2. 预编译(预处理):为编译做准备工作,完成代码文本的替换工作。
    3. 编译:形成目标代码(.obj)。
    4. 连接:将目标代码与C 函数库连接合并,形成最终的可执行文件。
    5. 执行:执行可执行文件。

    二、头文件

    1、头文件的作用

    头文件告诉编译器有这样一个函数,连接器负责找到这个函数的实现

    2、自定义头文件

    *注:开发工具为 Visual Studio 2017

    1、创建 .h 文件,对相应方法进行声明。

    例如:创建 math.h

    #ifndef _MATH_H  //如果没有定义 _MATH_H 标识
    #define _MATH_H  //定义 _MATH_H 标识
    int add(int, int, int);
    #endif
    

    //该头文件只被包含一次,让编译器自己处理好循环包含问题
    #pragma once 
    int add(int, int, int);
    

    2、在 .h 文件同级目录下创建对应的 .c 文件,对 .h 文件中声明的方法进行实现。

    例如:创建 head.c

    #define _CRT_SECURE_NO_WARNINGS
    #include "math.h"
    #include <stdio.h>
    
    int add(int a, int b, int c){
       int result = 0;
       result = a + b + c;
       return result;
    }
    
    

    3、创建一个C文件,进行验证头文件是否编写成功。

    例如:创建 test.c

    #include<stdio.h>
    #include "math.h"
    
    
    void main(){
        int a = 3, b = 4, c = 5, result = 0;
        result = add(a, b, c);
        printf("The result is %d!\n", result);
        system("pause");
    }
    

    三、define 指令

    1、define 指令的作用

    1. define 指令用来定义标识;
      如: #ifdef __cplusplus 标识支持C++语法;防止文件重复引入
    2. define 指令用来定义常数;如:#define MAX 100
    3. define 指令用来定义“宏函数”。
      如:
    void jni_read(){
        printf("read\n");
    }
    
    void jni_write(){
        printf("write\n");
    }
    
    //宏函数
    #define jin(NAME) jni_##NAME();
    
    void main(){
        jni(read);
        jni(write);
        getchar();
    }
    

    日志输出示例:

    //__VA_ARGS__    可变参数
    #define LOG(FOTMAT,...) printf(##FOTMAT,__VA_ARGS__);printf("\n");
    
    void main(){
        LOG("%s: %d","size",99);
        getchar();
    }
    

    四、JNI (Java Native Interface)

    1、定义

    **Java 调用C/C++或者C/C++调用 Java 的一套 API **

    2、Java调用C/C++项目开发步骤(Windows系统下)

    • 编写native方法
    package com.example.jni;
    public class JNITest {
    
        public native static String getStringFromC();
        
        public static void main(String[] args){
    
        }
    }
    
    • javah命令,生成.h文件
    javah com.example.jni.JNITest
    //生成 com_example_jni_JNITest.h 文件
    
    • 复制.h头文件到CPP工程中
    • 复制jni.h和jni_md.h文件到CPP工程中
    • 实现.h头文件中声明的函数;C函数名称:Java_完整类名_函数名
    //JNITest.c
    #include "com_example_jni_JNITest.h"
    
    JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_getStringFromC
    (JNIEnv *jEnv, jclass jcls) {
        //简单实现,将C的字符传转成Java的字符串
        return (*jEnv)->NewStringUTF(jEnv, "C String");
    
    }
    
    • 生成动态库.dll文件(Windows环境下默认dll,Linux环境下默认为so)
    • 配置D:\dll 目录到环境变量,并将刚刚生成的 .dll 文件复制到D:\dll 目录下;或者复制到项目根目录下;
    • 重启Eclipse,使用IDEA的需要在项目运行配置中的 VM options 中增加配置:
    // VM options:
    -Djava.library.path=D:\dll
    

    五、JNIEnv

    1、JNIEnv 是什么

    • 在C语言中JNIEnv是一个结构体指针,代表Java运行环境,主要是调用Java中的代码,在上面JNITest.c中实现函数声明的时候,jEnv 是一个二级指针
    //JNITest.c
    #include "com_example_jni_JNITest.h"
    
    JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_getStringFromC
    (JNIEnv *jEnv, jclass jcls) {
        //简单实现,将C的字符传转成Java的字符串
        return (*jEnv)->NewStringUTF(jEnv, "C String");
    
    }
    
    • 在C++中JNIEnv是一个结构体的别名,代表Java运行环境,主要是调用Java中的代码,jEnv 是一个结构体的一级指针
    //JNITest.cpp
    #include "com_example_jni_JNITest.h"
    
    JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_getStringFromC
    (JNIEnv *jEnv, jclass jcls) {
        //简单实现,将C的字符传转成Java的字符串
        return jEnv->NewStringUTF("C String");
    
    }
    

    模拟C 的实现

    //JNIEnv 是结构体指针的别名
    typedef struct JNINativeInferface_* JNIEnv;
    //结构体
    struct JNINativeInferface_{
        char* (*NewStringUTF)(JNIEnv*,char*);
    };
    
    //函数实现
    char* NewStringUTF(JNIEnv* env,char* str){
        return str;
    }
    
    void main(){
        //实例化结构体
        struct JNINativeInferface_ struct_env;
        struct_env.NewStringUTF = NewStringUTF;
        
        //结构体指针
        JNIEnv e = &struct_env;
        //结构体的二级指针
        JNIEnv *env = &e;
        //通过二级指针调用函数
        char* str = (*env)->NewStringUTF(env,"Hello");
        printf("str = %s\n",str);
        getchar();
    }
    
    

    2、JNIEnv 调用函数时C和C++的区别

    • C 中需要传入 JNIEnv ,因为函数执行过程中需要 JNIEnv
    • C++ 中不需要传入 JNIEnv ,是因为C++中有 this,相当与JNIEnv
    • C++只是针对C的那一套进行分装,给一个变量赋值为指针,这个变量是二级指针

    六、jclass

    每个native函数(C中的函数),都至少有两个参数(JNIEnv* jclass或者jobject)。

    • 当native方法为静态方法时:jclass代表native 方法所属类的class对象(JNITest.class);
    • 当 native 方法为非静态方法时:jobject 代表 native 方法所属的对象。

    相关文章

      网友评论

        本文标题:(一)JNI 开发流程

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