I have the following setup - a MainActivity with button which starts SDLActivity (SDL2). On the C++ side of my SDL project I have a main.cpp with declared native function:
extern "C" void Java_org_libdsl_app_SDLActivity_nativeSetAcc (JNIEnv* env, jclass clazz, jint Acc);
void Java_org_libdsl_app_SDLActivity_nativeSetAcc (JNIEnv* env, jclass clazz, jint Acc)
{
SDL_Log ("set acc");
// does something with the Acc value
}
I've put the following in the SDLActivity.java:
public static native void nativeSetAcc (int Acc);
but I'm getting unsatisified link error (java.lang.UnsatisfiedLinkError: Native method not found: org.libsdl.app.SDLActivity.nativeSetAcc:(I)V)
The C/SDL side compiles OK ("jni.h" is included as well). The android part runs fine until I want to use nativeSetAcc;
The strange part is that other JNI functions from SDL library do indeed work (nativeQuit, nativeResume, etc). And I'm sure that I do LoadLibrary ("main") - the code inside main's main() is working (looping SDL events, etc).
Looking at the hexdump of libmain.so I do see the Java_org_libdsl_app_SDLActivity_nativeSetAcc string.
Please help! Surely I'm missing something small, but I can't see.
OK Guys, I'm stupid. Instead of libsdl I was using libdsl in the code. A little sleep at sunday is very recommended
Related
One of the ways to register native functions in JNI is by using the function RegisterNatives(). An example (although seemingly with some errors) can be found in the Android documentation here. Here's a code example demonstrating this technique:
#include <jni.h>
void JNICALL test(JNIEnv* const environment, jobject const objectOrClass) {
}
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* const vm, void* const reserved) {
JNIEnv* environment;
vm->GetEnv(reinterpret_cast<void**>(&environment), JNI_VERSION_1_6);
auto const classRef = environment->FindClass("com/example/test/MainActivity");
JNINativeMethod method;
method.name = "test";
method.signature = "()V";
method.fnPtr = reinterpret_cast<void*>(test);
environment->RegisterNatives(classRef, &method, 1);
return JNI_VERSION_1_6;
}
Using RegisterNatives() requires casting (non-member) function pointers to type void*, as shown above. According to this documentation, this isn't necessarily portable in C++. It may be guaranteed to work in some environments (such as POSIX), but the behavior is otherwise implementation-dependent.
I wouldn't expect this functionality to be exposed this way if it wasn't reliable, but does either JNI or Android offer any guarantees that this can be counted on to work? Is it just assumed that JNI and/or Android will always be running in environments where casting between function pointers and void* is supported?
EDIT:
After further consideration, it seems JNI would almost have to do something non-portable with respect to native functions, irrespective of the cast issue. The only information about the function JNI has (other than the address) is a string describing the argument and return types, so it seems unlikely that the void* will ever be cast back to a valid function pointer and used in the conventional way. Presumably the calls are instead made via low-level platform-specific code.
It still seems like a requirement that function-pointer-to-void* casts be well-behaved, so if anyone knows if or how JNI guarantees that, I'd still be interested to know.
I need to get Android unique device ID in my native library. As far as i know, it can be done with Java API and i need to use JNI. I read this, there is similar problem, but different ID is accessed. But this solution needs reference to JNIEnv for getting Java objects/methods. When JNI method called from Java, this is not problem, JNIEnv will be passed from Java. But how i can get JNIEnv for "total" native code?
The entrypoint of your native activity receives a struct android_app* as its argument. android_app contains an ANativeActivity* named activity, which in turn contains a JNIEnv* named env.
To be able to call Android Java API methods from your native code you'll have to attach the current thread to the VM first, i.e.:
JNIEnv *env = state->activity->env;
JavaVM *vm = state->activity->vm;
(*vm)->AttachCurrentThread(vm, &env, NULL);
(where state is the struct android_app*)
I found that JNIEnv can be accessed with JavaVM, which can be created with JNI invocation API. By default in Android NDK this functions not exported to user(from platforms/android-19/arch-arm/usr/include/jni.h):
/*
* VM initialization functions.
*
* Note these are the only symbols exported for JNI by the VM.
*/
#if 0 /* In practice, these are not exported by the NDK so don't declare them */
jint JNI_GetDefaultJavaVMInitArgs(void*);
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
#endif
But this functions presented in /system/lib/libdvm.so on my device. After including function definition for JNI_CreateJavaVM in my code and linking to libdvm, copied from device, i got access to creation of JavaVM in Android. This is not recommended, of course, but, if you want it and your device libdvm supports this, you can do it.
I have compiled FFmpeg library using NDK and use it to trim videos and get thumbnails from a video in my app, so basically I have ffmpeg.so and video-trimmer.so libraries up and running.
The problem however is strange, the trim or getThumbnail operations are successful but just one time i.e. the first time and the operations fail the second time. However it is successful the third time, I googled it and got two similar posts on SO related to my problem
POST 1:
POST 2:
Interestingly they suggest the same solution and I am unable to solve the issue being a naive in C programming language.
Here is what I have done
void Java_com_example_demo_natives_LibraryLoader_loadTrimmerLibrary(JNIEnv* env, jclass class, jstring libffmpeg_path, jstring inputFile, jstring outFile,
jstring startTime, jstring length)
{
const char* path;
void* handle;
int *(*Java_com_example_demo_natives_VideoTrimmer_trim)(JNIEnv *, jclass, jstring, jstring, jstring, jstring);
path = (*env)->GetStringUTFChars(env, libffmpeg_path, 0);
handle = dlopen(path, RTLD_LAZY);
Java_com_example_demo_natives_VideoTrimmer_trim = dlsym(handle, "Java_com_example_demo_natives_VideoTrimmer_trim");
(*Java_com_example_demo_natives_VideoTrimmer_trim)(env, class, inputFile, outFile, startTime, length);
(*env)->ReleaseStringUTFChars(env, libffmpeg_path, path);
dlclose(handle);
}
Despite of calling dlclose the library instance still exists in memory, what I am doing wrong here?
I come to know that library instance still exists because when I load the libraries again in some other activity the error message says library already exists in CL.
I want to get rid of the instance of that library from memory, please help...
try moving the position of the 'ReleaseString...'
it should be after the 'dlopen'
it should be before the call into the other shared lib...
(*env)->GetStringUTFChars
dlopen
(*env)->ReleaseStringUTFChars
make the main call
dlclose
I try to use the pocketsphinx package for my app and need to rename the demo package name to something usefull (eg com.myname.foo)
I spent hours on figuring out, but I simply can't get it to work.
The problem is, that the project runs fine if I leave the package name and works without any problems (apart from random crashes), but when I rename it, i get the error
FATAL EXCEPTION: main
java.lang.UnsatisfiedLinkError: new_Config__SWIG_0
I already tried modifying the Swig command, but it didn't work either.
Any ideas?
I only changed the Manifest's package name declariation and the package folder of the normal Activity.
You need to change on the c/c++ side there are two posible ways depending on how your JNI is implemented.
A. The function name contains the full classpath
JNIEXPORT jlong JNICALL Java_"package with underscore instead of .""class""method"(JNIEnv *env, jclass class,...
e.g.
JNIEXPORT jlong JNICALL Java_com_android_mms_transaction_NativeSms_send(JNIEnv *env, jclass class,...
match method send in class NativeSms in package com.android.mms.transaction
B. There is a string supplied back to dalvik/javaVM with the classpath. Look for someting like this:
static int registerMethods(JNIEnv* env) {
static const char* const kClassName =
"com/example/android/platform_library/PlatformLibrary";
jclass clazz;
/* look up the class */
clazz = env->FindClass(kClassName);
if (clazz == NULL) {
LOGE("Can't find class %s\n", kClassName);
return -1;
}
/* register all the methods */
if (env->RegisterNatives(clazz, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
{
LOGE("Failed registering methods for %s\n", kClassName);
return -1;
}
...
Edit 2011-12-07 Clarified first example
Sooo, I found the problem; I spend 20 ****ing hours just to find out, that I actually forgot to add
static {
System.loadLibrary("pocketsphinx_jni");
}
to the Activity class. I can't believe I didn't see that, but thanks for all the answers! +1 for everyone helping me :]
If you have link command issue it's most likely you forgot to change the SWIG launch properties. The file is
.externalToolBuilders/SWIG.launch
Those properties have several places to mention edu.cmu.sphinx package.
If you changed something it's recommended to describe the changes more precisely. Most likely you just forgot some small thing. For example you can pack whole changed code into archive and upload it somewhere.
I want to use the OpenCV Android porting, that you can find HERE, to make some image transformations for an Augmented Reality application. I've found no problem configuring and building the library, I receive no error and I succed put it within my Android application throght JNI process: the library libopencv.so is in the correct directory "\libs\armeabi\" under my project's directory.
And now the problems:
1) First I want to understand what version of the original openCV library this porting derive from. Is important for me know if it derive from version 1.5, 2.0 or 2.1 because same functions are very different and others are absent.
2) Before starting with real time video manipulation, I'd try make some simple operations on a single image or saved video:
JNIEXPORT
jstring
JNICALL
Java_org_examples_testOpenCV_OpenCV_LoadImage(JNIEnv* env, jobject thiz)
{
IplImage* imgIn = cvLoadImage("/sdcard/testimage.jpg", -1);
if (!imgIn) return env->NewStringUTF("Error");
cvReleaseImage( &imgIn );
return env->NewStringUTF("Ok");
}
JNIEXPORT
jstring
JNICALL
Java_balmas_examples_testOpenCV_OpenCV_manageVideo(JNIEnv* env, jobject thiz)
{
CvCapture* capture = cvCaptureFromFile("/sdcard/video_galaxyspica_352x288_15fps.3gp");
if (!capture) return env->NewStringUTF("Error");
return env->NewStringUTF("Ok");
}
In both cases I receive "Error". There are no problems with files on the sdcard becouse I try to make this:
FILE* file = fopen("/sdcard/video_galaxyspica_352x288_15fps.3gp","w+");
//FILE* file = fopen("/sdcard/testimage.jpg","w+");
if (!file) return env->NewStringUTF("Error");
else {
fflush(file);
fclose(file);
return env->NewStringUTF("OK");
}
and I receive "OK".
I realize that there is some problem within highgui library but I don't understand what and wath I should make to avoid the problem.
Some suggestions!!!
Thank you everyone
guys- you may want to try this link, it ports the C++ 'modern' interface to opencv. The IplImage stuff is deprecated, but new versions leave wrappers if you need to support legacy code.
http://code.google.com/p/android-opencv/
There's a sample camera-calibration app, you click snap a few times and it will solve for the K matrix.
Note: you'll need the crystax ndk for STL classes, http://www.crystax.net/android/ndk-r4.php