JNI学习笔记:第一个JNI程序



1. 什么是JNI?

1.1 JNI简介

JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。

1.2 JNI的角色

  JNI可以这样与本地程序进行交互:
  1. 你可以使用JNI来实现“本地方法”(native methods),并在JAVA程序中调用它们。
  2. JNI支持一个“调用接口”(invocation interface),它允许你把一个JVM嵌入到本地程序中。本地程序可以链接一个实现了JVM的本地库,然后使用“调用接口”执行JAVA语言编写的软件模块。
这里写图片描述

2. 为什么要用JNI?

  JNI是JAVA平台的一个重要特征,使用它我们可以重用以前用C/C++写的大量代码。JNI的部分设计思想来源于Netscape的Java Runtime Interface(JRI)。使用JNI可以结合C++和Java两种语言的不同优势,使得我们的开发产品具有更强大的功能和更好的体验。

2.1 JNI的作用

  一方面,C++具有很好的执行效率和广泛的算法开源库,但是C++的开发成本和维护成本都过高,而另一方面,Java有更广的应用场景(Web开发、服务器开发、移动开发等),Java提供JNI接口,能够使C++代码运行在Java的环境中,使C++具有更好的应用场景,一方面能够将海量的C++开源库进行代码复用,减少了开发成本。
  举个例子,我们实验室的项目中,要用到OpenCV库做视频处理和运算,实验室之前的开发都是Windows桌面端的,使用OpenCV/C++作为开发语言。但是现在要转移到Android APP开发中,如果采用OpenCV for Android进行开发,可能面临两个问题:
  1. 之前做过的工作要重做,重复性建设;
  2. OpenCV for Android对一些算法的支撑并没有提供很好的支撑,仅提供了C++的接口(简直是逼着你用JNI接口),如VideoCapture类中,受限于Android设备的特征,并无法直接调用摄像头和处理帧数据,只能在C++层面对视频进行解析,以及相关运算和操作。
  如果使用JNI接口直接将原有的工作进行移植,那么一方面,可以代码复用,另一方面,也间接的解决了Java语言对算法支撑不足的问题,善莫大焉!

2.2 JNI的副作用

  请记住,一旦使用JNI,JAVA程序就丧失了JAVA平台的两个优点:
  1. 程序不再跨平台。要想跨平台,必须在不同的系统环境下重新编译本地语言部分。
  2. 程序不再是绝对安全的,本地代码的不当使用可能导致整个程序崩溃。

3. 第一个JNI程序 :A+B

3.1 开发环境

  • Visual Studio 2013 Ultimate
  • Java 8 64bit

3.2 程序流程

  我们用C++写一个A+B的函数,在VS中编译成dll动态链接库(Linux环境下是so库),然后在Java代码中加载该库,并传入A和B的值,在C++代码层计算并反回结果于Java层,完成一次数据传递。

3.3 Java代码

  1.先写Java代码,把需要的函数原型写出来,这里,我们定义C++做A+B的函数库叫做libmath,函数原型为 int addByCPP(int a,int b);

import java.util.Scanner;
import java.io.*;

public class Add{
    public native static int addByCPP(int a,int b);
    static{
        System.loadLibrary("libmath");
    }
    public static void main(String args[]){
        int a,b;
        Scanner sc = new Scanner(System.in);
        System.out.println("At java file,input a and b:");
        a = sc.nextInt();
        b = sc.nextInt();
        int c = addByCPP(a,b);
        System.out.println();
        System.out.print("At java file:a+b=" + c);
    }
}

其中,关键字native 定义了所声明的函数是JNI函数,System.loadLibrary 函数加载了其所在的动态链接库libmath

2.在cmd中使用javah命令生成对应的头文件
这里写图片描述
然后发现会多了一个Add.h文件
这里写图片描述
内容如下:

扫描二维码关注公众号,回复: 1880182 查看本文章
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>

/* Header for class Add */

#ifndef _Included_Add
#define _Included_Add
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Add
 * Method:    addByCPP
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_Add_addByCPP
  (JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

这个文件不用动,我们需要做的是写出该函数的函数实现

3.4 C++代码

  1. 在VS2013中创建空项目,注意项目名字最好是libmath,对应上一步的库名。
  这里写图片描述
  2. 项目属性配置
  在“项目属性” –> “VC++目录” –>“包含目录”中配置JNI文件的属性,路径是你的Java安装路径+\Java\jdk1.8.0_31\include `
  这里写图片描述
  在“项目属性” –> “常规” –> “配置类型”中选 动态库.dll
  这里写图片描述
  写函数体

#include"Add.h"
#include<jni.h>
#include<iostream>
using namespace std;

JNIEXPORT jint JNICALL Java_Add_addByCPP
(JNIEnv *, jclass, jint a, jint b){
    cout << "At cpp file: a = " << a << " b = " << b << endl;
    cout << "a+b = " << a + b;
    return (a + b);
}

  编译动态库,结果如下:
  这里写图片描述
  把dll库粘贴到Java代码的根目录下,开始运行
  这里写图片描述
  至此,第一个JNI程序,完成。

4. 参考文献

  [1]. 百度百科,“JNI”词条
  [2]. JNI Oracle官方参考文档
  [3]. 翻译文档:JNI 编程指南

猜你喜欢

转载自blog.csdn.net/cv_jason/article/details/79617598
今日推荐