-
Notifications
You must be signed in to change notification settings - Fork 747
The UnsatisfiedLinkError X File (a real experience)
So, you're getting a crash at runtime with that java.lang.UnsatisfiedLinkError
heading the stacktrace? And maybe that's happening in some devices but everything works like a charm in some others? Ok, we can solve that!
We also faced the same crash in our Android JavaCV based application. It worked really well in our Nexus 4, but it crashed in our Samsung S5. First of all, we need to be sure that we're adding the native libraries in the right way. For our project, our choice was the manual installation of the bunch of libraries:
- [your app or module dir]/libs/[javacpp,javacv,opencv].jar
- [your app or module dir]/src/main/jniLibs/[armeabi,armeabi-v7a,x86]/*.so
As you may guess, we used Android Studio (v2.1.1). We downloaded the JavaCV binaries from JavaCV Github home, copied the Java jars to the libs
directory, changed the extension of opencv-android-arm.jar
and opencv-android-x86.jar
to .tar
and after untar each file, we copied the *.so
files to the corresponding armeabi
and x86
arch directories. Also just make a plain copy of armeabi
to armeabi-v7a
.
You need to read this, it's a really good explanation of what is happening behind those java.lang.UnsatisfiedLinkError
crashes, but if you want to go straight to the point, add ReLinker to your project. Now, before any use of JavaCV (or the preset of your needs), make sure you're using ReLinker
in order to load the libraries, something like this:
ReLinker.Logger logger = new ReLinker.Logger() {
@Override
public void log(String message) {
Log.v("HODOR", "(hold the door) " + message);
}
};
ReLinker.log(logger).recursively().loadLibrary(context, "jniopencv_core");
ReLinker.log(logger).recursively().loadLibrary(context, "opencv_core");
ReLinker.log(logger).recursively().loadLibrary(context, "jniopencv_imgcodecs");
ReLinker.log(logger).recursively().loadLibrary(context, "opencv_imgcodecs");
ReLinker.log(logger).recursively().loadLibrary(context, "jniopencv_imgproc");
ReLinker.log(logger).recursively().loadLibrary(context, "opencv_imgproc");
In our case, we load each JavaCV library we import statically in our project, both jniopencv_*
and opencv_*
. Notice that we're removing the lib
prefix and the .so
sufix from the library file name.
Be careful, order of loaded libraries is important and have to respect dependencies. We can find the proper order by setting the org.bytedeco.javacpp.logger.debug
system property to true
and looking at the result in the log.
And... voilà! no more crashes in our lovely Galaxy S5.
One other situation where java.lang.UnsatisfiedLinkError
based crashes were happening on some Android devices happens when they come with a (normally older) version of some OpenCV libraries installed. This causes the problem when the version on the OS is not the same as the ones shipped with the app using JavaCV. We observed that some Samsung Galaxy devices, on some Android versions (4.4.2, 5.0.1), have a version of opencv_core
and opencv_imgproc
installed.
The normal loading process used by the JavaCV presets (Loader.load()
)will load the libraries installed with the OS instead of the ones shipped with the app, but will load the libraries shipped with the app that are not available on the OS - most notably jniopencv_core
, generating java.lang.UnsatisfiedLinkError
errors.
One workaround we found was to manually pre load the two libraries present on the OS, making sure that the version shipped with the app is used. That was accomplished using the following snipped, on the static initialisation of the class that uses JavaCV (it just needs to be run once):
URL[] emptyUrls = new URL[0];
Loader.loadLibrary(emptyUrls, "opencv_core");
Loader.loadLibrary(emptyUrls, "opencv_imgproc");
The empty array of URLs forces the loading process to use System.loadLibrary()
instead of System.load()
with a specific path, thus loading the library shipped with the app.
One other way of doing this, less dependent on the JavaCPP loading mechanism, but, because of that, not integrated with it, is directly preloading the libraries with System.loadLibrary()
:
System.loadLibrary("opencv_core");
System.loadLibrary("opencv_imgproc");
System.loadLibrary("jniopencv_core");