使用 PluginManagerThatSupportMultiLoader 遇到的相关问题
大佬, 上次提出的问题 #1053 修改后仍未达到自己想要的效果,因此尝试使用最优方案: PluginManagerThatSupportMultiLoader 来在同一 PPS 中对插件包进行隔离加载, 在使用调试过程中发现 PluginManagerThatSupportMultiLoader 存在以下问题:
onPluginServiceConnected方法仅在绑定 Service 成功后调用一次导致的mPpsController.setUuidManagerForPlugin()方法不会对不同的插件包设定不同的UuidManagerloadPluginLoader(uuid)方法中存在对mPluginLoader的判空校验,因此无法根据不同的 PluginKey 获得对应的mPluginLoaderonPluginServiceConnected方法中实现可进行优化,去除 mPluginLoader 的初始化
其中 1、2 两个问题比较严重,目前针对问题 ① 我的修改如下:
pps 绑定成功后启动其他的插件包时,手动设置 pluginKey 对应的 UuidManager 问题解决,但由于 UuidManagerBinder 为包内可见性,因此需要修改我的实现类对应的包名路径, 期望可以在 PluginManagerThatSupportMultiLoader 中提供方法供子类调用。
针对问题②, 修改如下:
去除 mPluginLoader 的非空校验,即可根据不同的 pluginKey 获取相应的 mPluginLoader, 同时也由于 BinderPluginLoader 可见性的限制, 需要修改实现类的包名路径, 期望 PluginManagerThatSupportMultiLoader 去除 mPluginLoader 的直接 非空校验,改为 map 将 pluginKey 与 mPluginLoader 进行映射再进行判空处理。
针对问题③, 因为目前 demo 中的 loadPluginLoaderAndRuntime 都大致按如下流程执行:
PPS 绑定成功后主要是对其设置 UuidManager, 至于其中的 mPluginLoader 的初始化,目前的 loadPluginLoader 方法中都已有相应的逻辑,仅仅需要对方法中的 iBinder 再加上非空校验即可,这样可使整体的逻辑更清晰,onPluginServiceConnected 的实现也保持了单一原则。
上述的①、②问题解决后,当 A 插件包启动成功后,切换启动 B 插件包遇到以下堆栈报错:

mPluginLoader.loadPlugin(partKey) 加载插件时我看相应的日志也没有问题:

希望大佬有时间的话可以看看该问题并能指定一二,目前不太清该问题应该如何进行排查?
目前不太清该问题应该如何进行排查?
这个之前回答过的:https://github.com/Tencent/Shadow/issues/1046#issuecomment-1244943401
像这样的Cannot cast A to B的错误可以这样排查:
- 确定Cannot cast 异常是根异常,没有进一步的caused by。
- 检查A的字节码,确认其.super字段确实是B或B的子类。
- 在运行时,拿到A的对象a,获取其ClassLoader,用这个加载A的ClassLoader查找B类。再拿到抛出cast异常的代码所属类的ClassLoader,用它也查找B类。对比两次查找到的B类是否是同一个对象。如果不是,注意它们的ClassLoader,进一步确定其所属的jar包、dex文件。这一检查流程关注的重点是JVM加载类的机制是用当前类的ClassLoader加载其引用的其他类。如果有多个ClassLoader中加载了同名的类,那它们对于JVM来说是不同的类。因此就算抛出异常Cannot cast X to X也是常见的情况。
我看异常产生时没有其他的问题产生,错误信息:com.youma.cjspro.PluginSplashActivity cannot be cast to com.tencent.shadow.core.runtime.PluginActivity, 我反编译查看 PluginSplashActivity 代码继承自 AppCompatActivity ... <----- CompentActivity <------ ShadowActivity。
然后查看具体错误发生时堆栈: at com.tencent.shadow.core.runtime.PluginActivity.get(Unknown Source:6)
查看源码最后到:ShadowActivityDelegate 的 getPluginActivity 方法,取得是 super.pluginActivity, 关键赋值代码如下:
override fun onCreate(savedInstanceState: Bundle?) {
......
val pluginActivity = mAppComponentFactory.instantiateActivity(
mPluginClassLoader,
pluginActivityClassName,
mHostActivityDelegator.intent
)
initPluginActivity(pluginActivity, pluginActivityInfo)
super.pluginActivity = pluginActivity
......
}
所以应该还是 ClassLoader 的原因
大佬, 我在一个 Issue 里看你介绍的关于 PluginManagerThatSupportMultiLoader 的使用方式: https://github.com/Tencent/Shadow/issues/961#issuecomment-1134393024
所以感觉还是我之前的使用方式没按您的设计来:
我是实现了 PluginManagerThatSupportMultiLoader 的子类,以为是只需要一个子类对象,然后调 PPS 的方法传入不同的 PluginKey 就可以实现插件中 runtime、loader 的加载以及拿到对应的 PluginLoader, 看了介绍的使用方式豁然开朗,因此 https://github.com/Tencent/Shadow/issues/1061#issue-1387505196 这里提到的前两点问题应该是不存在的,我来试试您设想的使用方式来进行使用看看。
至于我的想法应该也是可以实现相关需求,但经过自己几天调试运行,看到相关错误信息感觉自我能力还是不够,放弃设想的实现方案
使用 PluginManagerThatSupportMultiLoader 的不同子类对象启动不同的插件包,和我之前的实现方式表现还是一样,都只能单个插件包启动,切换插件包即出错,出错没有本质区别,主要有两个错误类型:
-
isIllegalIntent, 报错堆栈如下:
这里看过设置 loaderVersion的地方始终都为 local, 不能理解为何会被改为 2.3.0 ? -
之前的类转换异常:
xxx(启动的插件 Activity) cannot be cast to com.tencent.shadow.core.runtime.PluginActivity, 具体出错堆栈位置:
目前原因仍然未知,我看 PluginClassLoader 对象是没问题的,不知问题是否在 RuntimeClassLoader 与其之间?
这几天把 Shadow 插件化的原理给看了下,然后整个启动插件相关的源码(runtime、loader、以及插件的加载)都给看了一下,按相关设计修改了插件的实现,通过 delegateProviderKey 使 ShadowPluginLoader 与 PluginContainerActivity 一一对应,启动单个插件包时无问题,但切换启动另一个插件包时即会报错,具体报错堆栈如下:
Process: com.hl.baseproject.demo:pluginMulti, PID: 22307
java.lang.LinkageError: While checking class com.hl.my_runtime.CJSXTPluginProxyActivity method android.content.Context com.tencent.shadow.core.runtime.container.GeneratedHostActivityDelegator.createContext(android.content.ContextParams) signature against interface com.tencent.shadow.core.runtime.container.GeneratedHostActivityDelegator: Failed to resolve arg 0 type android.content.ContextParams with com.tencent.shadow.dynamic.host.MultiDynamicContainer$ContainerClassLoader (declaration of 'com.hl.my_runtime.CJSXTPluginProxyActivity' appears in /data/user/0/com.hl.baseproject.demo/files/ShadowPluginManager/UnpackedPlugin/my-plugin-manager/e6f9806a3c397d005654057abf8330b5/plugin-cjsxt-release.zip/my-runtime-release.apk)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)
at android.app.Instrumentation.newActivity(Instrumentation.java:1264)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3963)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4247)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:91)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:149)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:103)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2613)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:219)
at android.app.ActivityThread.main(ActivityThread.java:8668)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1109)
Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/content/ContextParams;
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)
at android.app.Instrumentation.newActivity(Instrumentation.java:1264)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3963)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4247)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:91)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:149)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:103)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2613)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:219)
at android.app.ActivityThread.main(ActivityThread.java:8668)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1109)
Caused by: java.lang.ClassNotFoundException: Didn't find class "android.content.ContextParams" on path: DexPathList[[zip file "/data/user/0/com.hl.baseproject.demo/files/ShadowPluginManager/UnpackedPlugin/my-plugin-manager/4798719aa9a10676bf14eb6139a97302/plugin-xxtk-release.zip/my-runtime-release.apk"],nativeLibraryDirectories=[/system/lib64, /hw_product/lib64, /system/product/lib64, /prets/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:209)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)
at android.app.Instrumentation.newActivity(Instrumentation.java:1264)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3963)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4247)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:91)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:149)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:103)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2613)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:219)
at android.app.ActivityThread.main(ActivityThread.java:8668)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1109)
我在 DynamicPluginLoader 类的 startActivityInPluginProcess 方法中加上以下日志:
@Synchronized
fun startActivityInPluginProcess(intent: Intent) {
mUiHandler.post {
val hostClassLoader = mContext.javaClass.classLoader
val hostParentClassLoader = hostClassLoader.parent
val contextParamName = "android.content.ContextParams"
try {
val loadClass = hostClassLoader.loadClass(contextParamName)
Log.e(
TAG,
"startActivityInPluginProcess: 宿主的 ClassLoader 加载到的 ContextParams ==" + loadClass
)
} catch (e: Exception) {
e.printStackTrace()
}
try {
val loadClass1 = hostParentClassLoader.loadClass(contextParamName)
Log.e(
TAG,
"startActivityInPluginProcess: 宿主的 parentClassLoader 加载到的 ContextParams ==" + loadClass1
)
} catch (e: Exception) {
e.printStackTrace()
}
mContext.startActivity(intent)
}
}
不管是否正常启动插件,宿主的 ClassLoader 以及它的 parent(加载 runtime 后即为 RuntimeClassLoader)都不能加载 android.content.ContextParams:
java.lang.ClassNotFoundException: Didn't find class "android.content.ContextParams"
所以使用 PluginManagerThatSupportMultiLoader 切换插件包时进行启动和单独启动插件包里的插件 mContext.startActivity(intent) 后续的逻辑好像有区别,不太清楚此问题如何定位查找,还请大佬有时间告知一二
#1118 与该问题报错一致,需要验证