上一篇:【插件化系列之处理资源加载异常的问题】

布景

  • 处理完资源加载报错的问题后,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 加载异常的问题

  • 我这边又去下载了其他游戏的安装包,发现里面的so库是能够正常加载的,原因是它加载so库的时候是用了别的一种方法,System.loadLibrary(Ps:最常见加载so库的便是用这种方法),和前面的游戏采用的方法不同。

插件化系列之解决 so 加载异常的问题

插件化系列之解决 so 加载异常的问题

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

插件化系列之解决 so 加载异常的问题

插件化系列之解决 so 加载异常的问题

插件化系列之解决 so 加载异常的问题

二、问题结论:这个游戏加载的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

插件化系列之解决 so 加载异常的问题

结束,撒花 ✿✿ヽ(▽)ノ✿