How to use .so library in android - java

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);

Related

Full Native Implementation

I am designing an application through JNI in order to prevent third party edits. I have moved forth towards registering native methods in order to remove jni bridge linkages, but as you can see, methods with override attribute still need to exist within the java code as native linked. Is there a way to fully port the remaining java code for this specific file?
Java:
#Override
protected native void onCreate(Bundle savedInstanceState);
CPP:
void onCreate(JNIEnv *env, jobject classObject, jobject bundle) {
/**super.onCreate**/
gObjects[0] = env->NewGlobalRef(classObject);
gClasses[0] = env->GetObjectClass(gObjects[0]);
jclass s_Class = env->GetSuperclass(gClasses[0]);
jmethodID oc_ID = env->GetMethodID(s_Class, "onCreate", "(Landroid/os/Bundle;)V");
env->CallNonvirtualVoidMethod(gObjects[0], gClasses[0], oc_ID, bundle);
}
extern "C" JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *curVM_, void *reserved) {
curVM = curVM_;
curVM->GetEnv((void **) &environments[0], JNI_VERSION_1_6);
/**Start of Native Method Registers**/
JNINativeMethod natives[1];
uint64_t pCount = sizeof(natives) / sizeof(natives[0]);
jclass klass = environments[0]->FindClass("com/company/app/Activity");
natives[0] = {"onCreate", "(Landroid/os/Bundle;)V", (void *)onCreate};
environments[0]->RegisterNatives(klass , natives, pCount);\
for (uint64_t i = 0; i < pCount; i++) natives[i] = {"", ""};\
return JNI_VERSION_1_6;
}
It does seem quite silly to think I can simply remove the native linkage between the activity and lib. Instead, I will be utilizing BaseDexClassLoader to sideload my dex file during runtime. I will not be giving up my code, but further information about this can be founded at: https://developer.android.com/reference/dalvik/system/BaseDexClassLoader

JNI: UnsatisfiedLinkError

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.

Java JNI UnsatisfiedLinkError

I'm trying to expose C++ functionality to Java using JNI. I'm trying to get a simple message box to show up first, just to make sure everything works. However, I'm getting an UnsatisfiedLinkError exception being thrown (the exception is being thrown when I call the function, not)
Java class (project cpplib):
package src;
public class MessageBox {
static {
System.loadLibrary("cpplib");
}
private static native void libf_show(String message, String caption);
public static void show(String message, String caption) {
libf_show(message, caption);
}
}
Note: the folder of cpplib, the DLL, has been added into the native libraries path
C++ header messagebox.hpp:
#pragma once
#include "jni.h"
extern "C"
{
JNIEXPORT void JNICALL Java_cpplib_src_MessageBox_show(JNIEnv *env, jstring jstr_message, jstring jstr_caption);
}
C++ source messagebox.cpp:
#include "messagebox.hpp"
#include <windows.h>
JNIEXPORT void JNICALL Java_cpplib_src_MessageBox_show(JNIEnv *env, jstring jstr_message, jstring jstr_caption)
{
const char *message = env->GetStringUTFChars(jstr_message, 0);
const char *caption = env->GetStringUTFChars(jstr_caption, 0);
MessageBox(NULL, message, caption, MB_OK);
env->ReleaseStringUTFChars(jstr_message, message);
env->ReleaseStringUTFChars(jstr_caption, caption);
}
Full error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: src.MessageBox.libf_show(Ljava/lang/String;Ljava/lang/String;)V
at src.MessageBox.libf_show(Native Method)
at src.MessageBox.show(MessageBox.java:11)
at src.CPPLIB_Test.main(CPPLIB_Test.java:6)
I believe I'm using the 64-bit version of the JDK (as a 32-bit DLL didn't work), so I'm using the appropriate 64-bit JDK headers (if they are different at all).
Why doesn't Java like my DLL?
I tried to reproduce your problem but I get different header. If a recreate your MessageBox.java inside a src directory and compile with javac -d build src/MessageBox.java and finally obtain C/C++ headers with javah -d include -classpath build src.MessageBox
Then, I got this method signature
JNIEXPORT void JNICALL Java_src_MessageBox_libf_1show (JNIEnv *, jclass, jstring, jstring);
instead
JNIEXPORT void JNICALL Java_cpplib_src_MessageBox_show(JNIEnv *env, jstring jstr_message, jstring jstr_caption);
How are you creating your C++ header? maybe here it is the problem.

JNI does not work with __stdcall

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);
}

How do I prepare my .cpp files for the Android ndk to build them?

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.

Categories