JNI實現(xiàn)JAVA調(diào)用C/C++開源庫

一、前言

JNI(Java Native Interface)的作用是實現(xiàn)java調(diào)用C/C++寫的方法或開源庫。由于java語言自身的局限性,一些特定功能的開源庫往往是沒有java語言版本的,比如本人項目中需要用到DSP庫,DSP庫C/C++的開源庫數(shù)不勝數(shù),但是JAVA版本的根本找不到,但是我的項目需要開發(fā)出一款app,因此必須使用java編程。為了解決這個矛盾,JNI就派上用場了。
本文將結(jié)合基于C++的DSP開源庫SP++3.0中的傅立葉變換方法講解JNI編程方法和常見錯誤及其解決方法。

二、具體過程

1.編寫java代碼

本文涉及兩個java文件,一個聲明java native方法,另一個含有主函數(shù)main,代碼如下:

class testjava
{
//java文件只需要聲明方法即可,具體實現(xiàn)在c或cpp文件中
public native double[] fft(double[] xn); //返回的數(shù)組的前半部分是傅立葉系數(shù)的實部,
//后半部分是虛部,方法需用native修飾
static {//必須用static修飾
    System.loadLibrary("javadll");//加載動態(tài)庫dll,不需要后綴名
}
}

public class main
{
public static void main(String[] args){
  testjava fourier=new testjava();
  int len=100;
    double[] x=new double[len];
    int j=0;
    for(double t=0;t<len;t++){
        x[j++]=Math.cos(2*Math.PI*t);
    }
    double[]Xk=new double[2*len];
    Xk=fourier.fft(x);//調(diào)用fft方法
    for(int i=0;i<len;i++){
        System.out.println(Xk[i]+"  "+Xk[i+len]);
    }
}
}

2.編譯testjava.java,并生成.h頭文件

本文全程使用cmd命令行,使用IDE方法類似,具體操作有細微區(qū)別

2.1 cd命令切換至java文件所在目錄后使用javac命令編譯,生成testjava.class

cd D:\java
javac testjava.java

2.2 使用javah命令生成testjava.h頭文件

這里需要注意的是,如果java文件所在路徑?jīng)]有添加進classpath環(huán)境變量中,cmd命令為

javah -classpath . testjava//注意中間有一個英文句號

3.編寫C/C++實現(xiàn)

3.1 頭文件添加

首先要把需要用到的頭文件jni.h,jni_md.h(jni.h需要用到),testjava.h,以及和傅立葉變換方法有關(guān)的fftpf.h,vectormath.h復(fù)制到cpp文件所在路徑,當(dāng)然也可以放到 VS2010的安裝路徑\VC\include。其中jni.h、jni_md.h在JDK安裝路徑下的include文件夾里

3.2 方法實現(xiàn)

javadll.cpp
#include <iostream>
#include <cstdlib>
#include<stdio.h>

#include <jni.h>
#include <fftpf.h>
#include <vectormath.h>
#include "testjava.h"

using namespace std;
using namespace splab;

JNIEXPORT jdoubleArray JNICALL Java_testjava_fft
  (JNIEnv *env, jobject jo, jdoubleArray xn){
  jsize len=env->GetArrayLength(xn); //獲取數(shù)組長度
  jdouble *p = env->GetDoubleArrayElements(xn,0); 
  Vector<double> signal(len,p);
  FFTPF<double> Fourier;
  Vector<complex<double>>  XK;
  XK.resize(len);
  Fourier.fft(signal,XK);
  Vector<double> a;
  Vector<double> b;
  a.resize(len);
  b.resize(len);
  a=real(XK);
  b=imag(XK);
  jdoubleArray result;
  result=env->NewDoubleArray(2*len);
  jdouble *p2 = env->GetDoubleArrayElements(result,0);  
  for(int i=0;i<XK.size();i++){
      p2[i]=a[i];
  }
  for(int i=XK.size();i<2*len;i++){
      p2[i]=b[i-XK.size()];
  }
  env->ReleaseDoubleArrayElements(xn,p,0);
  env->ReleaseDoubleArrayElements(result,p2,0);
  return result;
}

對比我們在testjava.java和testjava.h中的函數(shù)聲明(如下),我們發(fā)現(xiàn)java中的double[]變?yōu)榱薺doubleArray,這里怎么理解呢?可以這樣理解,在C/C++中,既可以使用C/C++原本的數(shù)據(jù)類型,又可以使用jdoubleArray這些數(shù)據(jù)類型。其他數(shù)據(jù)類型的對應(yīng)關(guān)系以及jint、jObjectArray、jsize這類數(shù)據(jù)的操作方法見附件(很好的一份文檔)。特別要注意的是文檔中提到的C和C++語法的差異,比如javadll.cpp中有注釋的一行,如果是使用C語言,則應(yīng)寫為

  jsize len=(*env)->GetArrayLength(env,xn); //獲取數(shù)組長度

很好理解,這是因為C++是面向?qū)ο蟮模蓄愡@一概念

testjava.h中的函數(shù)聲明
JNIEXPORT jdoubleArray JNICALL Java_testjava_fft
  (JNIEnv *, jobject, jdoubleArray);//testjava.h中輸入?yún)?shù)是沒有參數(shù)名的,
//在javadll.cpp中實現(xiàn)方法時應(yīng)當(dāng)加上參數(shù)名

testjava.java中的函數(shù)聲明
public native double[] fft(double[] xn);

4.編譯cpp文件得到動態(tài)鏈接庫文件javadll.dll

cd命令切換到VS2010安裝路徑\VC\bin\amd64,之后輸入vcvars64并回車啟動編譯器
再切換至cpp文件所在路徑,之后使用cl命令生成dll文件

cl -LD javadll.cpp

之后可以在cpp所在路徑下看到生成了javadll.dll,javadll.lib等文件
在這個過程中,需要注意的是dll的位數(shù)和所安裝的JDK的位數(shù)要一致,否則下一步運行時會報錯。
具體來說,VS2010的安裝路徑下有兩個編譯器,分別是32位和64位的,64位的路徑見上面,32位的路徑為VS2010的安裝路徑\Common7\Tools\vcvars32
如果你的JDK是64位的,就要使用vcvars64,否則使用vcvars32。JDK版本可以通過命令行的java -version命令獲得,若執(zhí)行命令后出現(xiàn)64-bits,則為64位,沒出現(xiàn)的為32位。
如果你是使用IDE,不是cmd,怎么辦呢?見文末附件。

5.運行java程序

首先要把javadll.dll復(fù)制到j(luò)ava文件所在路徑并切換至java文件所在路徑,其次用javac命令編譯,java命令運行。
javac命令編譯時注意,testjava.java和main.java需要一起編譯,否則會報錯。

javac *.java

最后運行,

java -classpath . main

引入-classpath的原因和javah命令一樣
運行截圖:

cos(2*pi*t)的傅立葉系數(shù)

6.附件

1.附件1講解了JNI的常見操作方法
http://www.doc88.com/p-403985462945.html
2.附件2是Youtube視頻,講解了cmd模式下的jni編程示例
https://www.youtube.com/watch?v=tDhOPYi-rYE&spfreload=1
3.附件3是本人自己寫的用VS2010創(chuàng)建dll的過程以及Eclipse中如何修改只單個項目所用的JDK版本而不用改變本機的JDK版本。
鏈接: https://pan.baidu.com/s/1o8A0OOi 密碼: 238d

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容