I am using the Java Native Interface to access several native methods within a DLL I created in C++. I can load the library without any problems, but I can only call the methods when they are located in the Java application’s Main Class. In other words everything works perfectly as long as I declare the methods and place the loadLibrary command in the Main Class.
However, if I attempt to place the loadLibrary command, and declare the native methods in a separate class, I get the following error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: sdr_api.SDR_API_Interface.SetDebugFlag_Native(Z)V
at sdr_api.SDR_API_Interface.SetDebugFlag_Native(Native Method)
at sdr_api.SDR_API_Interface.SetDebugFlag(SDR_API_Interface.java:74)
at sdr_api.SDR_API.main(SDR_API.java:183)
I’ve tried looking through the JNI documentation to check whether there is a requirement that all native methods must be declared in the main class, but I can’t find anything. I would like to move these methods into a separate class, as I need to use them in a separate thread (I only need one instance of the class, so they could be declared static if necessary).
My best guess is that declaring the methods in a particular class somehow changes the manner in which they are called within the DLL, but I don’t know how.
Any help would be greatly appreciated!
EDITED: Added relevant code samples
SDR_API Class:
package sdr_api;
public class SDR_API
{
public static void main(String[] args)
{
try
{
double Rate = 2e5;
double TxRxFreq = 5e9;
double RxGain = 0;
double TxGain = 0;
SDR_API_Interface mySDRAPI = new SDR_API_Interface();
mySDRAPI.SetDebugFlag(true);
mySDRAPI.Setup_BS_Config("addr=192.168.10.2",Rate,Rate,TxRxFreq,TxRxFreq,RxGain,TxGain);
mySDRAPI.Setup_MT_Config("addr=192.168.20.2",Rate,Rate,TxRxFreq,TxRxFreq,RxGain,TxGain);
}
catch(Exception e)
{
System.err.println("Exception thrown in main program: "+e);
}
}
}
SDR_API_Interface Class:
package sdr_api;
public class SDR_API_Interface
{
private native boolean Setup_MT_Config_Native(String _IP_Address,
double _Rx_Rate,
double _Tx_Rate,
double _Rx_Freq,
double _Tx_Freq,
double _Rx_Gain,
double _Tx_Gain);
private native boolean Setup_BS_Config_Native(String _IP_Address,
double _Rx_Rate,
double _Tx_Rate,
double _Rx_Freq,
double _Tx_Freq,
double _Rx_Gain,
double _Tx_Gain);
private native void SetDebugFlag_Native(boolean _DebugFlag);
private native double[] TransmitUL_Native(double[] _TxWaveform);
private native double[] TransmitDL_Native(double[] _TxWaveform);
private boolean Debug_Flag = false;
static
{
System.loadLibrary("SDR_API");
}
public boolean Setup_MT_Config(String _IP_Address,
double _Rx_Rate,
double _Tx_Rate,
double _Rx_Freq,
double _Tx_Freq,
double _Rx_Gain,
double _Tx_Gain)
{
return Setup_MT_Config_Native(_IP_Address,
_Rx_Rate,
_Tx_Rate,
_Rx_Freq,
_Tx_Freq,
_Rx_Gain,
_Tx_Gain);
}
public boolean Setup_BS_Config(String _IP_Address,
double _Rx_Rate,
double _Tx_Rate,
double _Rx_Freq,
double _Tx_Freq,
double _Rx_Gain,
double _Tx_Gain)
{
return Setup_BS_Config_Native(_IP_Address,
_Rx_Rate,
_Tx_Rate,
_Rx_Freq,
_Tx_Freq,
_Rx_Gain,
_Tx_Gain);
}
public void SetDebugFlag(boolean _DebugFlag)
{
Debug_Flag = _DebugFlag;
SetDebugFlag_Native(Debug_Flag);
}
public boolean GetDebugFlag()
{
return Debug_Flag;
}
}
JNI Machine Generated header file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class sdr_api_SDR_API */
#ifndef _Included_sdr_api_SDR_API
#define _Included_sdr_api_SDR_API
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: sdr_api_SDR_API
* Method: Setup_MT_Config_Native
* Signature: (Ljava/lang/String;DDDDDD)Z
*/
JNIEXPORT jboolean JNICALL Java_sdr_1api_SDR_1API_Setup_1MT_1Config_1Native
(JNIEnv *, jobject, jstring, jdouble, jdouble, jdouble, jdouble, jdouble, jdouble);
/*
* Class: sdr_api_SDR_API
* Method: Setup_BS_Config_Native
* Signature: (Ljava/lang/String;DDDDDD)Z
*/
JNIEXPORT jboolean JNICALL Java_sdr_1api_SDR_1API_Setup_1BS_1Config_1Native
(JNIEnv *, jobject, jstring, jdouble, jdouble, jdouble, jdouble, jdouble, jdouble);
/*
* Class: sdr_api_SDR_API
* Method: SetDebugFlag_Native
* Signature: (Z)V
*/
JNIEXPORT void JNICALL Java_sdr_1api_SDR_1API_SetDebugFlag_1Native
(JNIEnv *, jobject, jboolean);
/*
* Class: sdr_api_SDR_API
* Method: TransmitUL_Native
* Signature: ([D)[D
*/
JNIEXPORT jdoubleArray JNICALL Java_sdr_1api_SDR_1API_TransmitUL_1Native
(JNIEnv *, jobject, jdoubleArray);
/*
* Class: sdr_api_SDR_API
* Method: TransmitDL_Native
* Signature: ([D)[D
*/
JNIEXPORT jdoubleArray JNICALL Java_sdr_1api_SDR_1API_TransmitDL_1Native
(JNIEnv *, jobject, jdoubleArray);
#ifdef __cplusplus
}
#endif
#endif
@Ryan
On carefully analyzing the header file you can see the name of native method is
sdr_api_SDR_API_SetDebugFlag_Native
and the call from SDR_API_Interface is-
sdr_api.SDR_API_Interface.SetDebugFlag_Native
Probably you have created the header file with a different class
i.e SDR_API which is under the package sdr_api.
which should be actually created with the Interface class-
sdr_api.SDR_API_Interface.SetDebugFlag_Native
All you need to do is compile the class SDR_API_Interface, create the header file and then create the new dll to use it.
The dll you are using is still referencing a call to native method from the SDR_API class.
It should be SDR_API_Interface class.
Let me know if you still have doubts.