上一篇:【插件化系列之处理资源加载异常的问题】
布景
- 处理完资源加载报错的问题后,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;
}
}
}
- 加上之后问题被处理,非常 nice