I have integrated two native libraries (.so ) in my application. The libraries compile fine and I can load them in my application too. The first time I invoke a native method of a library it works fine, but if I call the same method again in the Activity the application shuts down.
The problem I am facing is exactly the same as mentioned in here :
http://grokbase.com/t/gg/android-ndk/1226m68ydm/app-exit-on-second-native-call
The solution that works is to invoke the native method in another Activity and shut it down forcefully via System.exit(0). Following the article I tried setting the pointers to NULL of the called method after a successful operation, but this too didn't help me. Also its not possible to unload a library once its loaded by System.loadLibrary().
I want to call the native methods more than once without creating a new Activity. Any ideas how to solve this issue ?
(I FINALLY FOUND A SOLUTION ... HERE IT IS)
Okay, I have finally found a way to resolve this issue. The solution is actually pretty simple. Build another independent native library (utility library) to load and unload the other libraries. What we need to do is use dlopen() and dlclose() in the native method of the utility. We can load the utility library like before via System.loadLibrary().
So in the native method of the utility library what we need to do is:
Use#include <dlfcn.h>
// this is required to call dlopen() and dlclose() functions.
Provide handler and function prototype:
void *handle;
typedef int (*func)(int); // define function prototype
func myFunctionName; // some name for the function
Open the library via dlopen() :
handle = dlopen("/data/data/my.package.com/lib/somelibrary.so", RTLD_LAZY);
Get and Call the function of the library:
myFunctionName = (func)dlsym(handle, "actualFunctionNameInLibrary");
myFunctionName(1); // passing parameters if needed in the call
Now that the call is done. Close it via dlclose():
dlclose(handle);
Hope this will help others facing the same issue.
So ... my solution was starting a service that runs the shared library code, this service has a different process name ( you can set it in the Android Manifest ), as it is a different process you can kill it ( Using Process.killProcess(Process.myPid()) when it finishes running, without affecting your application in any way.
Worked very well for me, hope it helps someone else.
As this is the top hit for this issue and as the issue itself still exists, it seems that the approach that ZakiMak shared with us is still the most popular solution.
For others who may want to implement it and would like a little more detail for the latest Android releases, here are some notes I made as I stumbled through this:
In the Java wrapping class:
import android.content.Context;
import android.util.Log;
public class FfmpegJNIWrapper {
//This class provides a Java wrapper around the exposed JNI ffmpeg functions.
static {
//Load the 'first' or 'outer' JNI library so this activity can use it
System.loadLibrary("ffmpeg_wraper_multi_invoke_jni");
}
public static int call_ffmpegWrapper(Context appContext, String[] ffmpegArgs) {
//Get the native libary path
String nativeLibPath = appContext.getApplicationInfo().nativeLibraryDir;
//Call the method in the first or 'outer' library, passing it the
//native library past as well as the original args
return ffmpegWrapper(nativeLibPath, ffmpegArgs);
}
// Native methods for ffmpeg functions
public static native int ffmpegWrapper(String nativeLibPath, String[] argv);
}
In the 'first' or 'outer' native library:
JNIEXPORT jint JNICALL Java_com_yourpackage_androidffmpegwrapper_FfmpegJNIWrapper_ffmpegWrapper(JNIEnv *pEnv, jobject pObj, jstring nativeLibPath, jobjectArray javaArgv) {
//Get the second or 'inner' native library path
char* nativePathPassedIn = (char *)(*pEnv)->GetStringUTFChars(pEnv, nativeLibPath, NULL);
char ourNativeLibraryPath[256];
snprintf(ourNativeLibraryPath, sizeof (ourNativeLibraryPath), "%s%s", nativePathPassedIn, "/libffmpeg_wraper_jni.so"); //the name of your ffmpeg library
//Open the so library
void *handle;
typedef int (*func)(JNIEnv*, jobject, jobjectArray);
handle = dlopen(ourNativeLibraryPath, RTLD_LAZY);
if (handle == NULL) {
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "could not open library: %s", dlerror());
printf("Could not dlopen(\"libbar.so\"): %s\n", dlerror());
return(-1);
}
//Call the ffmpeg wrapper functon in the second or 'inner' library
func reenterable_ffmpegWrapperFunction;
reenterable_ffmpegWrapperFunction = (func)dlsym(handle, "Java_com_yourpackage_androidffmpegwrapper_FfmpegJNIWrapper_ffmpegWrapper");
reenterable_ffmpegWrapperFunction(pEnv, pObj, javaArgv); //the original arguments
//Close the library
dlclose(handle);
// return
return(1);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With