Tencent / Shadow

零反射全动态Android插件框架
BSD 3-Clause "New" or "Revised" License
7.36k stars 1.29k forks source link

多插件运行时崩溃问题 #1318

Closed yaoyue1019 closed 1 month ago

yaoyue1019 commented 2 months ago

实际应用中集成shadows时需要加载多个不同的plugin,使用了PluginManagerThatSupportMultiLoader来替换demo中的FastPluginManager 希望startActivity能够在同一个进程中实现于是吧enter中的

                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    mPluginLoader.startActivityInPluginProcess(intent);

改成

((Activity) context).startActivityForResult(intent, bundle.getInt("requestCode")); // 我这里用的是startActivityForResult而不是startActivity

加载A插件并调用正常 加载B插件并调用正常 再次加载A插件发现异常 经过调试发现是 com.tencent.shadow.core.runtime.container.PluginContainerActivity#isIllegalIntent返回false 实际运行的时候 表达式中的这个返回false

processVersion != DelegateProviderHolder.sCustomPid

跟踪后调用发现

com.tencent.shadow.core.runtime.container.DelegateProviderHolder
    static {
        sCustomPid = SystemClock.elapsedRealtime();
    }

这个static语句被初始化了两次,分别是在AB这两个插件被初始化的时候调用的 是否可以考虑将DelegateProviderHolder.sCustomPid的初始化值交由host来设置

shifujun commented 2 months ago

一个loader本来就可以加载多个plugin的。custompid就是为了防止你这样误用时难以定位问题的。

yaoyue1019 commented 2 months ago

一个loader本来就可以加载多个plugin的。custompid就是为了防止你这样误用时难以定位问题的。

请问调用过程中是什么地方不正确呢

shifujun commented 2 months ago

sample-app和sample-base就是两个插件,可以参考下。

你说的static域执行了两遍,肯定是有什么更混乱的事发生了。不建议你继续调试multiloader的实现。即便你把代码发上来,我调试明白具体问题,也没什么可复用的意义。

yaoyue1019 commented 2 months ago

sample-app和sample-base就是两个插件,可以参考下。

你说的static域执行了两遍,肯定是有什么更混乱的事发生了。不建议你继续调试multiloader的实现。即便你把代码发上来,我调试明白具体问题,也没什么可复用的意义。

先前FastPluginManager使用PluginManagerThatUseDynamicLoader,单独打开A插件或者B插件都正常,但是我使用A插件后再使用B插件,发现mPluginLoader.convertActivityIntent(pluginIntent);返回不正确 原因是mPluginLoader会从第一次打开的插件中找loader,然后从这个动态加载的loader中查询Activity对应的intent,从错误的插件中找自然不到对应的intent,最后导致intent没有被正确convert,导致activity打开失败 请问我是什么地方漏了关键步骤或者有什么实现不正确吗

shifujun commented 2 months ago

你可以先debug sample-app和sample-base的运行情况。看看多个插件是怎么加载到loader里的,intent是怎么转换的,转换后的intent又是怎么加载插件的。

加载多个插件就是依次构造出classloader,resource等让loader持有着。再把插件的组件信息放到map里持有着。所以加载完的插件不存在再去原先插件apk里查找信息的情况了。

多debug sample应用再debug你有问题的业务场景。

yaoyue1019 commented 2 months ago

你可以先debug sample-app和sample-base的运行情况。看看多个插件是怎么加载到loader里的,intent是怎么转换的,转换后的intent又是怎么加载插件的。

加载多个插件就是依次构造出classloader,resource等让loader持有着。再把插件的组件信息放到map里持有着。所以加载完的插件不存在再去原先插件apk里查找信息的情况了。

多debug sample应用再debug你有问题的业务场景。

我这里实现中的多插件和demo里的多插件有点不太一样;demo里即使有多个插件,仍然只有一个runtime和一个loader,多个插件被打包在一个plugin.zip中,我本地集成的时候,会有多个的plugin.zip,所以也会有多个的loader和runtime

刚才恢复了之前的代码,跟踪断点发现加载多插件时会使用旧插件loader的原因是 pluginManager在convertActivityIntent时,使用到com.tencent.shadow.dynamic.loader.impl.DynamicPluginLoader#loadPlugin方法 然而loadPlugin方法在获取installedApk的时候的实现是val installedApk = mUuidManager.getPlugin(mUuid, partKey),这里的mUuid只在构造的时候初始化一次,加载了第一个插件是正常的,加载第二个插件时mUuid就是上一个插件的,因此在调用loadPlugin(partKey: String)的时候肯定返回值不正确

所以我觉得我可能还是得用回PluginManagerThatSupportMultiLoader,请问一下,之前的回答中不建议我使用PluginManagerThatSupportMultiLoader的原因是什么,是这个类放弃维护了吗?

shifujun commented 2 months ago

PluginManagerThatSupportMultiLoader是用来在同一个进程加载多个Loader的,你可以在提交记录里看到。你只是要加载多个插件,所以你用不上的。

我本地集成的时候,会有多个的plugin.zip,所以也会有多个的loader和runtime

即便你打包多个plugin.zip,它也只是个zip包。你没必要发布多个loader和runtime,更不应该分配不同的uuid。

你可能太快上手复杂的场景了,没太理解sample那种简单的场景下整个系统的工作流程。

yaoyue1019 commented 1 month ago

PluginManagerThatSupportMultiLoader是用来在同一个进程加载多个Loader的,你可以在提交记录里看到。你只是要加载多个插件,所以你用不上的。

我本地集成的时候,会有多个的plugin.zip,所以也会有多个的loader和runtime

即便你打包多个plugin.zip,它也只是个zip包。你没必要发布多个loader和runtime,更不应该分配不同的uuid。

你可能太快上手复杂的场景了,没太理解sample那种简单的场景下整个系统的工作流程。

感谢指导,尝试给多个插件分配了相同的uuid,并且把loader与runtime剥离出来 单独load多个插件都是正常,load一个插件后load第二个插件会提示 java.lang.IllegalStateException: java.lang.RuntimeException: 重复添加 ContentProvider 十分不惑 我的插件中并没有使用到ContentProvider,只有在loader中才有ContentProvider,然而loader现在只有一份了,为什么还是会提示重复添加

yaoyue1019 commented 1 month ago

经过反复比对,我发现我的多插件不能运行的原因是获取installedPlugin之后loadPlugin的问题 如果在此时load多个plugin,可以正常访问多个插件 如果在此时每次只load单独一个plugin,并且多次load不通的plugin,就会出现重复添加 ContentProvider的异常 我这里先看看有没有办法处理,如果需要一次loadPlugin所有的plugin,可能会和我的使用场景有所不符

shifujun commented 1 month ago

loadplugin可以分多次进行。完全就只是启动之前先加载好插件就行。所谓load/加载,就是解析一下插件的manifest信息,放到map里。启动好application oncreate入口。建议多debug sample的启动过程,不是很复杂的。

可以debug那个none-dynamic的例子,以便略过那些跨进程转调的复杂性。