Skip to content

The UnsatisfiedLinkError X File (a real experience)

Ian Ribas edited this page Jan 3, 2017 · 3 revisions

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