JNI (Java Native Interface,Java本地接口)是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。 本地程序一般是用其它语言(C、C++或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序。
Java调用C/C++大概有这样几个步骤:
- 编写带有native方法的Java类, 使用javac工具编译Java类
- 使用javah来生成与native方法对应的头文件
- 实现相应的头文件, 并编译为动态链接库(windows下是 .dll,linux下是 .so,macOS下是 .jnilib)
注意点:
- 依赖于JNI的应用失去了Java的平台移植性(一种解决办法是为每个平台编写专门的JNI代码,然后在Java代码中,根据操作系统加载正确的JNI代码)。
- JNI框架并没有对 non-JVM 内存提供自动垃圾回收机制,Native代码(如汇编语言)分配的内存和资源,需要其自身负责进行显式的释放。
我想用java做一些图像处理的工作,需要用到opencv,这里就以此为例.
1.编写Java类
1. 新建一个.java文件,内容如下:
package com.quickcan.watermark.util;
public class ImageProcessing {
static {
System.loadLibrary("image-processing");
}
public native void displayImage();
public static void main(String[] args) {
ImageProcessing imageProcessing = new ImageProcessing();
imageProcessing.displayImage();
}
}
2. 在ImageProcessing.java所在的目录下执行
javac ImageProcessing.java
会在当前目录下生成一个ImageProcessing.class
3. 如果ImageProcessing.java有一个package(在这里是com.quickcan.watermark.util),则需要进入这个package的根目录(在这里是com)的上级目录,执行:
javah com.quickcan.watermark.util.ImageProcessing
生成com_quickcan_watermark_util_ImageProcessing.h
内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_quickcan_watermark_util_ImageProcessing */
#ifndef _Included_com_quickcan_watermark_util_ImageProcessing
#define _Included_com_quickcan_watermark_util_ImageProcessing
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_quickcan_watermark_util_ImageProcessing
* Method: displayImage
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_quickcan_watermark_util_ImageProcessing_displayImage
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
该文件定义了一个方法:Java_com_quickcan_watermark_util_ImageProcessing_displayImage,这个方法对于java文件中的native方法:public native void displayImage();.JNI在C语言中定义的规则是Java_包名_类名_方法名
2.编写C++©代码
1. 新建一个目录,用来编写.cpp(或者.c).将系统*/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/include/darwin/* 目录下的两个文件:jawt_md.h,jni_md.h复制过来,再将上一步生成的com_quickcan_watermark_util_ImageProcessing.h也复制过来.
在当前目录下新创建一个.cpp(或者.c)文件:image-processing.cpp(这个叫什么名字无所谓),用来实现com_quickcan_watermark_util_ImageProcessing.h中的方法
//
// Created by yshhuang on 2019-06-27.
//
#include "jni.h"
#include "com_quickcan_watermark_util_ImageProcessing.h"
#include <stdio.h>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <string>
using namespace cv;
using namespace std;
JNIEXPORT void JNICALL Java_com_quickcan_watermark_util_ImageProcessing_displayImage
(JNIEnv *env, jobject obj) {
String imageName("/Volumes/develop/tool/lena.jpg");
Mat image;
image = imread(imageName, IMREAD_COLOR);
if (image.empty()) {
cout << "Could not open or find the image" << std::endl;
}
cout << image << endl;
return;
};
编译这个文件:
g++ -dynamiclib -I /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/include/darwin/ $(pkg-config --cflags --libs opencv4) -std=c++14 image-processing.cpp -o libimage-processing.jnilib
2. 将上一步生成的libimage-processing.jnilib放到java.library.path下面(可以通过java代码:System.out.println(System.getProperty(“java.library.path”)); 获得).
### 3. 运行第一步创建的ImageProcessing.java的main方法,即可打印出图片各点的像素值.(如果是在IntelliJ IDEA中的java项目中运行,则把libimage-processing.jnilib放到项目的根目录也可以)