首页 > 解决方案 > 动态加载/重新加载 Java 类时防止重复 System.loadLibrary 调用

问题描述

我有一个为 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)
...

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

有没有办法卸载类/库?有没有办法检查库是否已经加载?

标签: javaandroidjava-native-interface

解决方案


Runtime.loadLibrary()应该忽略同一个库的后续加载,根据方法 javadoc

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

可能插件卸载不完整,它看起来像是ClassLoader 0x187从以前的插件生命周期中幸存下来的。最好检查一下这个ClassLoader对象实例是什么。

如果你得到一个,你可以尝试ClassLoader.findLibrary()并且只尝试加载库null

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


推荐阅读