I'm trying to test out some JNI code integrating a Java class with some ROS functionality and I'm struggling to get the Java methods linked up correctly. I've got the native code compiled against the JNI interface correctly (or so I think) but at runtime I get an UnsatisifiedLinkError on the first native method I have defined. At this point I'm not sure if the root cause is that the JVM isn't properly loading the .so file (in the same directory, and I've tried -Djava.library.path=.) or if it's successfully loading it and it's not finding the method correctly.
This error message gives so little to go on, is there a way to get more info about what exactly is causing it?
I'm not opposed to posting the source code if it would help, though I'd have to do some editing before I can upload it so I'll wait to see if you guys think it might be helpful.
Talker.java:
public class Talker {
/**
* ROS Native methods
*
* Simple passthrough to the C++ native methods in the ROS layer
*/
private static native void rosAdvertise();
private static native void rosPublish();
private static native void rosSpinOnce();
{
System.loadLibrary("ros-test-native-talker");
}
public static void main(String[] args) {
rosAdvertise();
while (true) {
rosPublish();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
rosSpinOnce();
}
}
}
ros-test-native-talker.cpp:
#include "test_rostest_Talker.h"
#include "ros/ros.h"
#include "std_msgs/Time.h"
ros::Publisher outbound;
JNIEXPORT void JNICALL Java_test_rostest_Talker_rosAdvertise
(JNIEnv *, jclass) {
int argc = 0;
char **argv;
ros::init(argc, argv, "ros-native-timing-tester");
ros::NodeHandle n;
outbound = n.advertise<std_msgs::Time>("chatter", 1000);
}
JNIEXPORT void JNICALL Java_test_rostest_Talker_rosPublish
(JNIEnv *, jclass) {
ros::Time tx_timestamp = ros::Time::now();
ROS_INFO("Sending message at %d.%d", tx_timestamp.sec, tx_timestamp.nsec);
std_msgs::Time msg;
msg.data = tx_timestamp;
outbound.publish(msg);
}
JNIEXPORT void JNICALL Java_test_rostest_Talker_rosSpinOnce
(JNIEnv *, jclass) {
ros::spinOnce();
}
and the output:
rush#lubuntu64vm:~/javarostest$ java -Djava.library.path=. -cp ros-test-native-1.0-SNAPSHOT.jar test.rostest.Talker
Exception in thread "main" java.lang.UnsatisfiedLinkError: test.rostest.Talker.rosAdvertise()V
at test.rostest.Talker.rosAdvertise(Native Method)
at test.rostest.Talker.main(Talker.java:21)
I have no clue why but refactoring the above code slightly caused it to work. If I take the native methods out of the main classand put them in a separate class (thus removing the static modifier on the native methods) which is invoked by the main class, it all links and works fine. I'm not certain nor do I even have a clue why, but I think the static modifier on those methods was causing some issues.
Related
I have a class containing a member variable of type long and a set of native method declarations. I believe the memory for the variable is being allocated in one of the native methods and the de-allocation is attempted in the finalize() method by calling another native method destroy. I understand that finalize is deprecated but prior to finding an alternative for finalize, I need to understand how the memory allocation is happening and how Java and C maintain sync through JNI and how this particular member variable is tracked. I will attempt to explain the scenario better with code snippets:
JSQ.java
class JSQ {
protected long shdl;
protected JSQ() { log("JSQ constructor"); }
protected JSQ(String stmt, boolean isd)
throws DhSQLException
{
// calls a set of native methods
set_shdl();
setstmt(stmt, shdl);
setd(isd, shdl);
prep(shdl);
}
public void finalize()
{
destroy(shdl);
}
private native void set_shdl() throws DhSQLException;
private native void setstmt(String s, long shdl) throws DhSQLException;
private native void setd(boolean b, long shdl);
private native void prep(long shdl) throws DhSQLException;
private native void destroy(long shdl);
protected void execute() throws DhSQLException
{
parExec(shdl);
}
protected native void parExec(long shdl);
}
JSQL.cxx
#define SQ ((sq_stsm_t *)(shdl))
JNIEXPORT void JNICALL Java_com_project_package_JSQ_set_shdl
(JNIEnv *env, jobject obj_This)
{
jclass cls;
jmethodID mid;
cls = (env)->GetObjectClass (obj_This);
mid = (env)->GetMethodID (hThis,"set_JSQ_shdl","(J)V");
status_t status;
// memory allocation
sq_stsm_t * S = new sq_stsm_t(status);
if(status)
{
if (S) { delete S; }
return;
}
// I understand that we're attempting to call a Java method from a native method.
// But which method is it calling?
// Also, are we converting S from sq_stms_t type to jlong?
(env)->CallVoidMethod (obj_This,mid,(jlong) S);
return;
}
JNIEXPORT void JNICALL Java_com_project_package_JSQ_setstmt
(JNIEnv *env, jobject, jstring jstmt, jlong shdl)
{
status_t status;
// cstmt is obtained using jstmt
// Note: #define SQ ((sq_stsm_t *)(shdl))
status = SQ->SetStmt(cstmt);
return;
}
JNIEXPORT void JNICALL Java_com_project_package_JSQ_destroy
(JNIEnv *, jobject, jlong shdl)
{
delete SQ;
}
The points where I'm confused:
Why long and jlong and the conversion from the user defined type sq_stsm_t to long. I understand that Java is not aware of this user defined type. But why the choice of long?
Why the #define on SQ and how exactly does destroy delete the memory allocated in set_shdl by calling delete on SQ?
Post this, I have to find a replacement for finalize - for which I have concluded that AutoCloseable with try-with-resources is, by far, my best option. Since there are a lot of native calls involved, I'm confused as to how I would go about it. But, that is a separate question to be addressed and I don't want to go into that here. Just mentioning to set some background on the use case.
A Java long variable is guaranteed to be 64 bits wide, so it is enough to contain an address, even on a 64-bit system.
The cast to long is just there to make the C++ type system happy.
As you see, the SQ macro is used to convert this address back to a proper pointer to an sq_stsm_t object.
As for your second question: all the macro does is get rid of some typing work.
The Java part of your program will always pass the long-typed shdl variable, so the SQ macro just provides easy access to the actual sq_stsm_t object.
Finally, new and delete in C++ go together: new allocates memory and calls the constructor, delete calls the destructor and frees the memory again.
Note that "free" does not necessarily mean "give back to the OS", so your process's memory usage can remain the same after this delete operation.
As for the comment in your code: Java_com_project_package_JSQ_set_shdl tries to call the Java method boolean com.project.package.JSQ#set_JSQ_shdl(jlong), although it is not present in the code you pasted. I guess it is a simple setter for the shdl field?
As for your follow-up plans: implementing AutoCloseable is a good idea. You would need to create a native void close() method in your JSQ object which calls delete on the sq_stsm_t object stored in the shdl field and then replaces the shdl field with (jlong)nullptr.
Am trying to call a x86 DLL that I created using VC 6, from a java project on Eclipse, first try I got an error saying that I can't call a x86 DLL from a x64 envirement and that the DLL can't be loaded. So I installed a x86 jre and I have no more problem to charge the DLL.
But when I try to call my c++ function I get the following exception:
Exception in thread "main" java.lang.UnsatisfiedLinkError: mm.SimpleDLL.SimpleDLL_Calculation_Add(II)I
Can someone please help me ?
Thank you.
SimpleDLL.h
#ifndef SIMPLE_DLL_H
#define SIMPLE_DLL_H
namespace SimpleDll
{
extern class Calculation
{
public:
static __declspec(dllexport) int Add(int a, int b);
};
}
#endif SIMPLE_DLL_H
SimpleDLL.cpp
#include "SimpleDll.h"
namespace SimpleDll
{
int Calculation::Add(int a, int b)
{ return a + b; }
}
SimpleDLL.java
package mm;
public class SimpleDLL {
static
{
System.load("D:\\SimpleDLL.dll");
}
public static void main(String ar[])
{
System.out.println("Hello world from Java");
SimpleDLL t=new SimpleDLL();
int x = t.SimpleDLL_Calculation_Add(6, 7);
System.out.println("Resultat "+x);
}
public native int SimpleDLL_Calculation_Add(int a, int b);
}
Exported DLL Functions View
Resolved using the JNA library, I used this link to walkthrough
You need to build a 32-bit DLL and a .h file using javah for the exact signature Java expects. It is usually something like
JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod
(JNIEnv *, jobject, jboolean);
from https://medium.com/#bschlining/a-simple-java-native-interface-jni-example-in-java-and-scala-68fdafe76f5f
An alternative approach is to use a library like JNA or JNR-FFI which allow you to bind to a C library without writing this bridging code.
I'm trying to use a python interpreter in my Android app to run SymPy. I already compiled Python for arm using this guide. http://mdqinc.com/blog/2011/09/cross-compiling-python-for-android/
This got me a libpython2.7.so file which I put into jniLibs/armeabi/.
In my app I load it as follows:
public class PythonTest {
static {
System.loadLibrary("python2.7");
}
public static native void Py_Initialize();
public static native void Py_Finalize();
public static native int PyRun_SimpleString(String s);
}
I am trying to use the methods from the headers located in the include directory which can also be found here: https://docs.python.org/2/c-api/
When I run the app on my device I get the following error:
No implementation found for void com.example.dorian.testapplication.PythonTest.Py_Initialize() (tried Java_com_example_dorian_testapplication_PythonTest_Py_1Initialize and Java_com_example_dorian_testapplication_PythonTest_Py_1Initialize__)
So to me this seems like the library loaded but it seems to look for the JNIEXPORT functions. But shouldn't I be able to use this library without writing specific C++ files? And if not, how would I accomplish this. Might there be a tool to generate wrapper files or something similar?
You need a JNI wrapper lib that will serve as a bridge between your Java code and libpython2.7.so. It may be enough for straters to have the three functions wrapped according to the JNI convention, e.g.
JNIEXPORT jint JNICALL com_example_dorian_testapplication_PythonTest_PyRun_1SimpleString
(JNIEnv *env, jclass jc, jstring js)
{
char* cs = env->GetStringUTFChars(js, 0);
std::string s = new std::string(cs);
env->ReleaseStringUTFChars(env, js, cs);
return PyRun_SimpleString(s.c_str());
}
If something is not clear, please read the tutorial at http://joaoventura.net/blog/2014/python-android-2.
Note that you can use any package name for the PythonTest class, not necessarily related to your Android app package name, e.g.
package com.python27;
class Python {
static {
System.loadLibrary("python2.7");
}
public static native void Initialize();
public static native void Finalize();
public static native int Run(String s);
}
will expect the JNI wrappers
JNIEXPORT void JNICALL com_python27_Python_Initialize(JNIEnv *env, jclass jc);
JNIEXPORT void JNICALL com_python27_Python_Finalize(JNIEnv *env, jclass jc);
JNIEXPORT jint JNICALL com_python27_Python_Run(JNIEnv *env, jclass jc, jstring js);
I'm playing around with JNI on Windows 7x64, Java version is 1.7.0_40 and MinGW / GCC / G++ 4.7.2.
Trying to power off my monitor from Java. So, I've created a class:
public class MonitorTrigger {
static {
try {
System.load(new ClassPathResource("MonitorTrigger.dll").getFile().getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
public native void on();
public native void off();
}
Then, generated h file from it (with Eclipse, but I beleive it uses javah):
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger */
#ifndef _Included_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
#define _Included_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
* Method: on
* Signature: ()V
*/
JNIEXPORT void
JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_on(
JNIEnv *, jobject);
/*
* Class: by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
* Method: off
* Signature: ()V
*/
JNIEXPORT void
JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_off(
JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
And implemented it:
#include "by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger.h"
#include <iostream>
#include <windows.h>
JNIEXPORT void JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_on
(JNIEnv * env, jobject object) {
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) -1);
}
JNIEXPORT void JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_off
(JNIEnv * env, jobject object) {
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) 2);
}
It does not work! I get exception:
Exception in thread "main" java.lang.UnsatisfiedLinkError: by.dev.madhead.bubikopf.desktop.monitortrigger.util.MonitorTrigger.off()V
at by.dev.madhead.bubikopf.desktop.monitortrigger.util.MonitorTrigger.off(Native Method)
at by.dev.madhead.bubikopf.desktop.monitortrigger.App.main(App.java:7)
I was stuck for a bit, but after googling, I've found a solution - use __cdecl instead of __stdcall convention for exported function. So, I've made a very dirty hack:
#define JNICALL __cdecl
And it works!
It was previously defined as __stdcall at jni_md.h. I realize that I'm doing a very bad thing, which harm kittens, but I lack of experience in C/ C++, and I cannot figure out, why I should redefine this? Why standard header (jni_md.h) defines this incorrectly?
For 32 bit DLLs being used by jni, the code has to be compiled without the usual __stdcall adornments - i.e. the code can't have the #N postfix on the symbol name; but it still needs to be compiled in __stdcall mode.
When compiling the dll under mingw, you need to add the option --kill-at to the linker. This is generally passed using -Wl,--kill-at. This will cause the #N postfix to be removed, and so appear as a simple symbol which can be linked at run-time by the JVM. The option can be abbreviated to -Wl,-k as well.
An alternative is to use a map file, which exports the symbols in their unmangled form, which is the mechanism that's used most often when compiling with microsoft's own visual studio compiler.
This is pretty poorly documented, and the resulting error you get when it happens doesn't help too well.
I'd recommend looking at JNA, which makes writing native code wrappers a lot simpler, and in this case would mean no C++ code needed.
The JNA code to accomplish this looks like:
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.WPARAM;
public class JNAMonitorTrigger {
public void monitor(LPARAM param) {
User32.INSTANCE.PostMessage(WinUser.HWND_BROADCAST,
WinUser.WM_SYSCOMMAND,
new WPARAM(0xf170), param);
}
public void on() {
monitor(new LPARAM(-1));
}
public void off() {
monitor(new LPARAM(2));
}
public static void main(String args[]) throws Exception {
JNAMonitorTrigger me = new JNAMonitorTrigger();
me.off();
Thread.sleep(1000);
me.on();
}
};
Even though you're on Windows, you're using GCC, which means this: Is there STDCALL in Linux? is still basically going to apply.
The functions in your file generated by java.h are declared as extern "C"
You have to add it in your cpp file :
extern "C"
JNIEXPORT void JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_on
(JNIEnv * env, jobject object) {
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) -1);
}
We currently have some image processing software written in c++ which is being used by our IOS application. I am trying to integrate this image processing code into the android project that I created using the Android NDK.
I have my android project and the sdk all setup and ready to go. I also have the ndk setup and ready to go.
I was following through on this tutorial (which is awesome), and I got stumped at the part that he defined the code for native.c because it had a function name like this,
void Java_com_mamlambo_sample_ndk1_AndroidNDK1SampleActivity_helloLog(JNIEnv * env, jobject this, jstring logThis)
It almost looks to me like I have to go through all of my existing c++ functions and alter the code in order for the NDK to recognize it.
So here are my questions,
Do I have to alter my existing c++ code in order for it to work with the ndk builder? And if so, what are the things I need to change in my code for this work?
Is there a way to have the Android.mk file build an entire directory? I have a lot of files and I did not want to have to list out every single one of them in order to get them built.
1) You should be able to build without alteration, but you will need to write some of those JNI wrapper functions to call it from Java. Hopefully you have a small number of top-level classes and you will only need to wrap those. E.g. Here's what I have for a game I'm (slowly) writing:
// android.cpp
#include "game.h"
#include <jni.h>
namespace {
Game* toGame(jlong gamePtr) {
return reinterpret_cast<Game*>(gamePtr);
}
}
extern "C" {
jlong Java_com_rarepebble_game3_Game_create(JNIEnv* env, jobject jobj) {
Game* g = new Game();
return reinterpret_cast<jlong>(g);
}
void Java_com_rarepebble_game3_Game_destroy(JNIEnv* env, jobject jobj, jlong gamePtr) {
delete toGame(gamePtr);
}
void Java_com_rarepebble_game3_Game_update(JNIEnv* env, jobject jobj, jlong gamePtr, jboolean isTouching, jfloat touchX, jfloat touchY) {
toGame(gamePtr)->update(isTouching, touchX, touchY);
}
void Java_com_rarepebble_game3_Game_render(JNIEnv* env, jobject jobj, jlong gamePtr) {
toGame(gamePtr)->render();
}
// ... and a few others. Only one class, though.
}
On the Java side this lets me declare those functions in my com.rarepebble.game3.Game class and call them at the appropriate times in the app's lifecycle. (Note how the Java package, class and function names correspond to the function names in C++):
//Game.java
package com.rarepebble.game3;
// (imports)
public class Game {
static {
System.loadLibrary("game3lib");
}
// These are the functions you defined in C++
private native long create();
private native void destroy(long gamePtr);
private native void update(long gamePtr, boolean isTouched, float x, float y);
private native void render(long gamePtr);
private long gamePtr = 0;
Game() {
gamePtr = create();
}
#Override
protected void finalize() throws Throwable {
if (gamePtr != 0) {
destroy(gamePtr);
}
super.finalize();
}
// etc...
}
2) You can use
LOCAL_SRC_FILES := $(wildcard *.cpp) $(wildcard subdirectory/*.cpp) #etc...
Edit: As requested, my C++ Game class header, "game.h":
// game.h
#ifndef GAME_INCLUDED
#define GAME_INCLUDED
// Various game and standard library includes.
// *NO* JNI or Android includes.
namespace game {
class Game {
public:
Game();
~Game();
void update(bool isTouching, float touchX, float touchY);
void render();
// other funcs...
private:
// etc...
};
}
#endif //def GAME_INCLUDED
Similarly, "game.cpp" doesn't include any JNI stuff either.