Preventing Duplicate System.loadLibrary calls when dynamically loading/reloading a Java class

Viewed 1787

I have a plugin system I am maintaining for an Android system. One of the plugins that is being loaded calls System.loadLibrary() in a static init block.

A plugin get's loaded like this:

Class<?> pluginClass = Class.forName(clsName, true, classLoader);

The first time this is called, everything works just fine.

If the plugin get's reloaded for any particular reason (disable/enable or, more importantly, a version upgrade), it tries to re-init the class and I get an error that looks like this:

2019-01-11 17:10:30.160 30764-31064/com.foobar W/PluginInstanceManager: Couldn't load plugin: com.foobar.plugin
    java.lang.UnsatisfiedLinkError: Shared library "/data/app/com.foobar.plugin-eVVPC94W5JcpK0yF00OfHw==/lib/arm64/liblibrary-jni.so" already opened by ClassLoader 0x187(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.foobar.plugin-eVVPC94W5JcpK0yF00OfHw==/base.apk"],nativeLibraryDirectories=[/data/app/com.foobar.plugin-eVVPC94W5JcpK0yF00OfHw==/lib/arm64, /data/app/com.foobar.plugin-eVVPC94W5JcpK0yF00OfHw==/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /system/product/lib64, /system/lib64, /vendor/lib64, /system/product/lib64]]]); can't open in ClassLoader 0x735fd03f9c(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.foobar.plugin-eVVPC94W5JcpK0yF00OfHw==/base.apk"],nativeLibraryDirectories=[/data/app/com.foobar.plugin-eVVPC94W5JcpK0yF00OfHw==/lib/arm64, /data/app/com.foobar.plugin-eVVPC94W5JcpK0yF00OfHw==/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /system/product/lib64, /system/lib64, /vendor/lib64, /system/product/lib64]]])
        at java.lang.Runtime.loadLibrary0(Runtime.java:1048)
        at java.lang.Runtime.loadLibrary0(Runtime.java:1007)
        at java.lang.System.loadLibrary(System.java:1669)
        at com.foobar.plugin.<clinit>(FoobarPlugin.java:31)
...

It seems to be complaining the that library has already been loaded and therefore I can't load the class again.

Is there a way to unload the class/library? Is there a way to check if the library is already loaded?

2 Answers

Runtime.loadLibrary() should ignore subsequent load of the same library, as per method javadoc:

If this method is called more than once with the same library name, the second and subsequent calls are ignored.

Potentially the plugin unloading is not complete, it looks like ClassLoader 0x187 survived from previous plugin lifecycle. It would be good to check what is this ClassLoader object instance.

You can try ClassLoader.findLibrary() and only try to load library if you get a null:

Returns the absolute path name of a native library. The VM invokes this method to locate the native libraries that belong to classes loaded with this class loader. If this method returns null, the VM searches the library along the path specified as the "java.library.path" property.

From the JNI documentation:

Each class loader manages its own set of native libraries. The same JNI native library cannot be loaded into more than one class loader. Doing so causes UnsatisfiedLinkError to be thrown. For example, System.loadLibrary throws an UnsatisfiedLinkError when used to load a native library into two class loaders.

To be able to use the native library in two plugins or two instances of the same plugin you have to make sure that it's loaded in some shared class loader.

Related