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

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

背景

  • 解决完资源加载报错的问题后,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

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

猜你喜欢

转载自juejin.im/post/7124519307651842078