动态加载/重新加载Java类时防止重复的System.loadLibrary调用

时间:2019-01-11 22:53:54

标签: java android java-native-interface

我有一个为Android系统维护的插件系统。正在加载的其中一个插件在静态初始化块中调用System.loadLibrary()

插件的加载方式如下:

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

第一次调用它,一切正常。

如果出于任何特定原因(禁用/启用,或更重要的是版本升级)重新加载了插件,它将尝试重新初始化该类,并且出现如下错误:

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)
...

似乎在抱怨该库已经被加载,因此我无法再次加载该类。

是否有一种方法可以卸载课程/库?有没有办法检查库是否已经加载?

2 个答案:

答案 0 :(得分:0)

Runtime.loadLibrary()应该忽略method javadoc中相同库的后续加载:

  

如果使用相同的库名称多次调用此方法,则第二次及以后的调用将被忽略。

可能是该插件的卸载尚未完成,看起来ClassLoader 0x187在以前的插件生命周期中仍然存在。检查一下这个ClassLoader对象实例是什么。

您可以尝试ClassLoader.findLibrary()并仅在获得null时尝试加载库:

  

返回本机库的绝对路径名。 VM调用此方法来查找属于使用此类加载器加载的类的本机库。如果此方法返回null,则VM沿指定为"java.library.path"属性的路径搜索库。

答案 1 :(得分:0)

来自edit

  

每个类加载器都管理自己的一组本机库。同一JNI本机库不能被加载到多个类加载器中。这样做会引发UnsatisfiedLinkError。例如,System.loadLibrary在用于将本机库加载到两个类加载器中时抛出UnsatisfiedLinkError。

要在两个插件或同一个插件的两个实例中使用本机库,必须确保已将其装入某些共享的类加载器中。