背景
- 解决完资源加载报错的问题后,Unity 开发人员(简称 CP)又给我们反馈了另外一个问题
Process: xxx.xxx, PID: 21699
java.lang.RuntimeException: Unable to start activity ComponentInfo{xxx.xxx.MainActivityFullScreen}: java.lang.IllegalArgumentException: Unable to find native library xgame using classloader: com.plugin.core.loader.ApkClassLoader[DexPathList[[zip file "/data/user/0/xxx.xxx.xxx/app_plugin/sq_plugin_xxxxxx.apk"],nativeLibraryDirectories=[/system/lib64]]]
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3177)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3314)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:113)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:71)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2043)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:224)
at android.app.ActivityThread.main(ActivityThread.java:7096)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:928)
Caused by: java.lang.IllegalArgumentException: Unable to find native library xgame using classloader: com.plugin.core.loader.ApkClassLoader[DexPathList[[zip file "/data/user/0/xxx.xxx.xxx/app_plugin/sq_plugin_xxxxxx.apk"],nativeLibraryDirectories=[/system/lib64]]]
at android.app.NativeActivity.onCreate(NativeActivity.java:161)
at org.evt.lib.EvtActivity.onCreate(EvtActivity.java:18)
at android.app.Activity.performCreate(Activity.java:7271)
at android.app.Activity.performCreate(Activity.java:7262)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3157)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3314)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:113)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:71)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2043)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:224)
at android.app.ActivityThread.main(ActivityThread.java:7096)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:928)
一、问题筛选过程
- 这个游戏采用的是 android.app.NativeActivity 类来加载 so 库,可以看到它是 getClassLoader (即获取到的是插件的 ApkClassLoader 对象), 然后进行 findLibrary 的操作,但是我们 ApkClassLoader 没有重写这个方法,即表现为只会查找插件中的 so 库,如果没有找到就报错。


- 我这边又去下载了其他游戏的安装包,发现里面的 so 库是可以正常加载的,原因是它加载 so 库的时候是用了另外一种方式,System.loadLibrary(Ps:最常见加载 so 库的就是用这种方式),和前面的游戏采用的方式不同。


- 于是我写了一段测试代码,直接调用 System.loadLibrary 进行 debug,实践证明并没有问题,因为它的 ClassLoader 和宿主的 ClassLoader 对象是同一个,自然是可以找得到。



二、问题结论:这个游戏加载的 so 库所用到的 ClassLoader 是插件的 ApkClassLoader,而插件的 ApkClassLoader 并没有重写 findLibrary 方法的逻辑,所以就会导致插件的 ApkClassLoader 找不到宿主的 so 库。
三、修复方案:重写 ApkClassLoader.findLibrary 方法,优先去找插件的 so 库,找不到就去找宿主的 so 库。
public class ApkClassLoader extends DexClassLoader {
private ClassLoader mGrandParent;
private Method mFindLibraryMethod;
public ApkClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super(dexPath, optimizedDirectory, librarySearchPath, parent);
mGrandParent = parent;
}
@Override
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
......
Class<?> clazz = findLoadedClass(className);
if (clazz == null) {
ClassNotFoundException suppressed = null;
try {
clazz = findClass(className);
} catch (ClassNotFoundException e) {
suppressed = e;
}
if (clazz == null) {
try {
clazz = mGrandParent.loadClass(className);
} catch (ClassNotFoundException e) {
e.addSuppressed(suppressed);
throw e;
}
}
}
return clazz;
}
// 加入以下代码
@Override
public String findLibrary(String name) {
String path = super.findLibrary(name);
if (path != null) {
return path;
}
if (mGrandParent instanceof BaseDexClassLoader) {
return ((BaseDexClassLoader) mGrandParent).findLibrary(name);
}
try {
if (mFindLibraryMethod == null) {
mFindLibraryMethod = ClassLoader.class.getDeclaredMethod("findLibrary", String.class);
mFindLibraryMethod.setAccessible(true);
}
// 如果插件获取不到,则交由父加载器进行获取
return (String) mFindLibraryMethod.invoke(mGrandParent, name);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}

完结,撒花 ✿✿ヽ(°▽°)ノ✿