我正在使用JNI开展一个学校项目。简而言之,我已经让第一部分使用共享库和JNI,但是第二部分(引用同一个库)每次尝试运行代码时都会产生UnsatisfiedLinkError。我的教授和助教均无法解决这个问题,而且我一直试图调试它一个多星期了。
第一部分是一个独立执行的.java程序,给定一个整数和一个频率的文件,它应该调用C中的本机代码来计算直接傅立叶变换并返回给定频率的值。在java中,我将这些int存储到一个数组中,并将数组传递给JNI来计算C中的DFT。这将返回一个int值并且像魅力一样工作。
对于第二部分,我们创建了一个gui接口,它可以加载JNI库并接收.wav文件或我们自己格式的文件。从文件选择器中选择时,需要此gui来计算文件的频率。由于.wav文件(和其他类型)有头信息和二进制数据,而不是第一部分中的整数,我的策略是将文件名(作为字符串)传递给JNI,以便我的本机代码可以将int值解析为数组本身,然后计算文件的频率。我的gui是完整的,从选择器中选择文件的动作事件应该调用JNI代码并返回文件的频率。有趣的是,代码在linux中编译并成功加载库,但在选择文件时应该使用此链接错误崩溃,并且应该使用该文件调用本机代码选择器。我完全失败了。有什么想法吗?
这是JNI生成的标题文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class dft */
#ifndef _Included_dft
#define _Included_dft
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: dft
* Method: computeDFT
* Signature: ([III)I
*/
JNIEXPORT jint JNICALL Java_dft_computeDFT
(JNIEnv *, jobject, jintArray, jint, jint);
/*
* Class: dft
* Method: computeFileDFT
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_dft_computeFileDFT
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
这是工作的代码
public class dft
{
private native int computeDFT(int[] nums, int count, int freq);
private native int computeFileDFT(String fileName);
static
{
/*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* SYSTEM LOAD LIBRARY PATH BELOW
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
System.loadLibrary("dft");
}
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException
{
File infile;
Scanner sc = null;
dft dft = new dft();
int count = 0;
int[] data = new int[4096];
//code that reads the file and puts the content into an array called 'data'
//the args[1] below is the specified frequency to be calculated in JNI
int dftValue = dft.computeDFT(data, count, Integer.parseInt(args[1]));
System.out.println(dftValue);
}
这是问题代码
private native int computeFileDFT(String fileName);
static
{
/*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* SYSTEM LOAD LIBRARY PATH BELOW
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
System.loadLibrary("dft");
System.out.println("loaded successfully");
}
//imagine more code here creating a JPanel object,
//adding a bunch of stuff to it, etc.
//One key item that is added is a button,
//which when pressed opens a JFileChooser object,
//below you will see my implementation of the
//ActionListener which listens to the button and is
//supposed to execute the native code when the file is chosen.
private class TunerButtonListener implements ActionListener
{
//@Override
public void actionPerformed(ActionEvent event)
{
int returnVal = dialog.showOpenDialog(fileButton);
if(returnVal == JFileChooser.APPROVE_OPTION)
{
currentFileName = dialog.getSelectedFile().getName();
currentFilePath = dialog.getSelectedFile().getPath();
message.setText(currentFileName);
int val = computeFileDFT(currentFilePath);
//more code goes here that does more stuff
完整的错误消息和堆栈
Exception in thread "AWT-EventQueue-0" java.lang.UnsatisfiedLinkError: TunerPanel.computeFileDFT(Ljava/lang/String;)I
at TunerPanel.computeFileDFT(Native Method)
at TunerPanel$TunerButtonListener.actionPerformed(TunerPanel.java:167)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2012)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2335)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:404)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:289)
at java.awt.Component.processMouseEvent(Component.java:6389)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3268)
at java.awt.Component.processEvent(Component.java:6154)
at java.awt.Container.processEvent(Container.java:2045)
at java.awt.Component.dispatchEventImpl(Component.java:4750)
at java.awt.Container.dispatchEventImpl(Container.java:2103)
at java.awt.Component.dispatchEvent(Component.java:4576)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4633)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4297)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4227)
at java.awt.Container.dispatchEventImpl(Container.java:2089)
at java.awt.Window.dispatchEventImpl(Window.java:2518)
at java.awt.Component.dispatchEvent(Component.java:4576)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:672)
at java.awt.EventQueue.access$400(EventQueue.java:96)
at java.awt.EventQueue$2.run(EventQueue.java:631)
at java.awt.EventQueue$2.run(EventQueue.java:629)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:116)
at java.awt.EventQueue$3.run(EventQueue.java:645)
at java.awt.EventQueue$3.run(EventQueue.java:643)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:642)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:275)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:200)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:185)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:177)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:138)
答案 0 :(得分:2)
阅读S.O.所有无所不在的聪明人的巨大评论主题。 JNI亚文化,我可能会遗漏一些东西,但对我来说这个bug很明显:
您已针对类dft
创建了本机JNI库,因此您的本机函数为Java_dft_*
。在dft
实例的范围内调用时确实有效。
然后您创建另一个类TunerPanel
。当然,您声明了相同的本机方法签名,但您没有任何具有Java_TunerPanel_*
函数的库!您仍然使用与Java_dft_*
相同的原始库。
从dft
访问TunerPanel
个实例。您不能对两个不同类的本机方法使用相同的本机库。或者将本机方法设为静态。就我对FFT的理解而言,您不需要在compute*
的运行之间保留任何持久数据。