Shadow icon indicating copy to clipboard operation
Shadow copied to clipboard

shadow插件中使用 EventBus产生的问题

Open fsdhr1 opened this issue 4 years ago • 8 comments

在shadow插件中使用Eventbus。代码如下: @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... EventBus.getDefault().register(this); }

启动后会报如下错误:

Process: com.tencent.shadow.sample.host:plugin, PID: 29900 java.lang.NoClassDefFoundError: android.app.PictureInPictureParams at libcore.reflect.InternalNames.getClass(InternalNames.java:55) at java.lang.Class.getDexCacheType(Class.java:2551) at java.lang.reflect.AbstractMethod.getParameterTypes(AbstractMethod.java:169) at java.lang.reflect.Method.getParameterTypes(Method.java:193) at java.lang.reflect.Method$1.compare(Method.java:72) at java.lang.reflect.Method$1.compare(Method.java:66) at libcore.util.CollectionUtils.removeDuplicates(CollectionUtils.java:89) at java.lang.Class.getMethods(Class.java:1423) at org.greenrobot.eventbus.SubscriberMethodFinder.findUsingReflectionInSingleClass(SubscriberMethodFinder.java:157) at org.greenrobot.eventbus.SubscriberMethodFinder.findUsingInfo(SubscriberMethodFinder.java:88) at org.greenrobot.eventbus.SubscriberMethodFinder.findSubscriberMethods(SubscriberMethodFinder.java:64) at org.greenrobot.eventbus.EventBus.register(EventBus.java:140) at com.gykj.commontool.locationservicetest.LocationServiceTestActivity.onCreate(LocationServiceTestActivity.java:36) at com.tencent.shadow.core.loader.delegates.ShadowActivityDelegate.onCreate(Unknown Source) at com.tencent.shadow.core.runtime.container.PluginContainerActivity.onCreate(PluginContainerActivity.java:84) at android.app.Activity.performCreate(Activity.java:6910) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2757) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2875) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1578) at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:156) at android.app.ActivityThread.main(ActivityThread.java:6618) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832) Caused by: java.lang.ClassNotFoundException: Didn't find class "android.app.PictureInPictureParams" on path: DexPathList[[zip file "/data/user/0/com.tencent.shadow.sample.host/files/ShadowPluginManager/UnpackedPlugin/sample-manager/7ebf44f2867797786e2f30966c82c6cb/plugin-debug.zip/sample-runtime-debug.apk"],nativeLibraryDirectories=[/data/user/0/com.tencent.shadow.sample.host/files/ShadowPluginManager/UnpackedPlugin/sample-manager/lib/E442EB4E-D377-4F4B-AC7A-57050F7820D7_lib, /system/lib64, /vendor/lib64, /system/vendor/lib64, /product/lib64]] at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) at java.lang.ClassLoader.loadClass(ClassLoader.java:380) at java.lang.ClassLoader.loadClass(ClassLoader.java:312) at com.tencent.shadow.dynamic.apk.ApkClassLoader.loadClass(ApkClassLoader.java:86) at java.lang.ClassLoader.loadClass(ClassLoader.java:312) at libcore.reflect.InternalNames.getClass(InternalNames.java:53) at java.lang.Class.getDexCacheType(Class.java:2551)  at java.lang.reflect.AbstractMethod.getParameterTypes(AbstractMethod.java:169)  at java.lang.reflect.Method.getParameterTypes(Method.java:193)  at java.lang.reflect.Method$1.compare(Method.java:72)  at java.lang.reflect.Method$1.compare(Method.java:66)  at libcore.util.CollectionUtils.removeDuplicates(CollectionUtils.java:89)  at java.lang.Class.getMethods(Class.java:1423)  at org.greenrobot.eventbus.SubscriberMethodFinder.findUsingReflectionInSingleClass(SubscriberMethodFinder.java:157)  at org.greenrobot.eventbus.SubscriberMethodFinder.findUsingInfo(SubscriberMethodFinder.java:88)  at org.greenrobot.eventbus.SubscriberMethodFinder.findSubscriberMethods(SubscriberMethodFinder.java:64)  at org.greenrobot.eventbus.EventBus.register(EventBus.java:140)  at com.gykj.commontool.locationservicetest.LocationServiceTestActivity.onCreate(LocationServiceTestActivity.java:36)  at com.tencent.shadow.core.loader.delegates.ShadowActivityDelegate.onCreate(Unknown Source)  at com.tencent.shadow.core.runtime.container.PluginContainerActivity.onCreate(PluginContainerActivity.java:84)  at android.app.Activity.performCreate(Activity.java:6910)  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2757)  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2875)  at android.app.ActivityThread.-wrap12(ActivityThread.java)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1578)  at android.os.Handler.dispatchMessage(Handler.java:105)  at android.os.Looper.loop(Looper.java:156)  at android.app.ActivityThread.main(ActivityThread.java:6618)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)  Suppressed: java.lang.ClassNotFoundException: Didn't find class "android.app.PictureInPictureParams" on path: DexPathList[[zip file "/data/user/0/com.tencent.shadow.sample.host/files/ShadowPluginManager/UnpackedPlugin/sample-manager/7ebf44f2867797786e2f30966c82c6cb/plugin-debug.zip/sample-loader-debug.apk"],nativeLibraryDirectories=[/data/user/0/com.tencent.shadow.sample.host/files/ShadowPluginManager/UnpackedPlugin/sample-manager/lib/E442EB4E-D377-4F4B-AC7A-57050F7820D7_lib, /system/lib64, /vendor/lib64, /system/vendor/lib64, /product/lib64]] at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) at com.tencent.shadow.dynamic.apk.ApkClassLoader.loadClass(ApkClassLoader.java:79) ... 28 more

fsdhr1 avatar Dec 17 '21 06:12 fsdhr1

看起来是EventBus的代码写了一些不安全的反射逻辑。找不到android.app.PictureInPictureParams这种系统类的情况,你可以在插件里自己定义一个这个名字的类来解决。

shifujun avatar Dec 17 '21 07:12 shifujun

这是因为EventBus会去寻遍历所有register的类(包括其父类),用来查找被@Subscribe注解标记的类,但是系统SDK的类将被忽略(看这里) 所以,最好的办法是忽略Shadow中代替Activity的类(ShadowActivity及其父类) 可以这么改: ` void moveToSuperclass() {

        if (skipSuperClasses) {
            clazz = null;
        } else {
            clazz = clazz.getSuperclass();
            String clazzName = clazz.getName();
            // Skip system classes, this degrades performance.
            // Also we might avoid some ClassNotFoundException (see FAQ for background).
            if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||
                    clazzName.startsWith("android.") || clazzName.startsWith("androidx.")
                    || clazzName.startsWith("com.tencent.shadow.core")) {
                clazz = null;
            }
        }
    }`

有能力的也可以通过插件用javassist来改这里

gb18030 avatar Dec 23 '21 08:12 gb18030

这是因为EventBus会去寻遍历所有类(包括其父类),用来查找被@subscribe注解标记的类,但是系统SDK的类将被忽略(看这里) 所以,最好的办法是忽略Shadow中代替Activity的类(ShadowActivity及其父类) 可以这么改: ` void moveToSuperclass() {

        if (skipSuperClasses) {
            clazz = null;
        } else {
            clazz = clazz.getSuperclass();
            String clazzName = clazz.getName();
            // Skip system classes, this degrades performance.
            // Also we might avoid some ClassNotFoundException (see FAQ for background).
            if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||
                    clazzName.startsWith("android.") || clazzName.startsWith("androidx.")
                    || clazzName.startsWith("com.tencent.shadow.core")) {
                clazz = null;
            }
        }
    }`

有能力的也可以通过插件用javassist来改这里

这段代码是不是也可以通过判断clazz的ClassLoader来区分是否是系统类呀?

shifujun avatar Dec 23 '21 11:12 shifujun

这是因为EventBus会去寻遍历所有类(包括其父类),用来查找被@subscribe注解标记的类,但是系统SDK的类将被忽略(看这里) 所以,最好的办法是忽略Shadow中代替Activity的类(ShadowActivity及其父类) 可以这么改: ` void moveToSuperclass() {

        if (skipSuperClasses) {
            clazz = null;
        } else {
            clazz = clazz.getSuperclass();
            String clazzName = clazz.getName();
            // Skip system classes, this degrades performance.
            // Also we might avoid some ClassNotFoundException (see FAQ for background).
            if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||
                    clazzName.startsWith("android.") || clazzName.startsWith("androidx.")
                    || clazzName.startsWith("com.tencent.shadow.core")) {
                clazz = null;
            }
        }
    }`

有能力的也可以通过插件用javassist来改这里

这段代码是不是也可以通过判断clazz的ClassLoader来区分是否是系统类呀?

我简单看了下框架代码 loader runtime 下的类由apkClassLoader加载 插件中的类由pluginClassLoader加载 一般只有业务代码中会用到eventBus 也就是说只要是eventbus只去检测pluginClassLoader加载的类就可以了

但是如果单独运行apk呢 这时候所有的类都是系统的ClassLoader加载啊 那是不是就要弄两套eventBus库啊

或者说忽略由apkClassLoader加载的类就可以

gb18030 avatar Dec 24 '21 02:12 gb18030

但是如果单独运行apk呢 这时候所有的类都是系统的ClassLoader加载啊 那是不是就要弄两套eventBus库啊

单独运行的时候,系统类也跟app的类不在一个ClassLoader。app的类正常在PathClassLoader里。插件运行时换成了PluginClassLoader。EventBus自己所在的ClassLoader就是app的ClassLoader。

shifujun avatar Dec 24 '21 03:12 shifujun

但是如果单独运行apk呢 这时候所有的类都是系统的ClassLoader加载啊 那是不是就要弄两套eventBus库啊

单独运行的时候,系统类也跟app的类不在一个ClassLoader。app的类正常在PathClassLoader里。插件运行时换成了PluginClassLoader。EventBus自己所在的ClassLoader就是app的ClassLoader。

那这么改应该更好一点

` void moveToSuperclass() {

        if (skipSuperClasses) {
            clazz = null;
        } else {
            clazz = clazz.getSuperclass();
            //String clazzName = clazz.getName();
            // Skip system classes, this degrades performance.
            // Also we might avoid some ClassNotFoundException (see FAQ for background).
            if (getClass().getClassLoader().getClass() != clazz.getClassLoader().getClass()) {
                clazz = null;
            }
        }
    }

`

gb18030 avatar Dec 24 '21 05:12 gb18030

但是如果单独运行apk呢 这时候所有的类都是系统的ClassLoader加载啊 那是不是就要弄两套eventBus库啊

单独运行的时候,系统类也跟app的类不在一个ClassLoader。app的类正常在PathClassLoader里。插件运行时换成了PluginClassLoader。EventBus自己所在的ClassLoader就是app的ClassLoader。

那这么改应该更好一点

` void moveToSuperclass() {

        if (skipSuperClasses) {
            clazz = null;
        } else {
            clazz = clazz.getSuperclass();
            //String clazzName = clazz.getName();
            // Skip system classes, this degrades performance.
            // Also we might avoid some ClassNotFoundException (see FAQ for background).
            if (getClass().getClassLoader().getClass() != clazz.getClassLoader().getClass()) {
                clazz = null;
            }
        }
    }

`

Hi,这个问题您最后是怎么解决的啊,通过javassist修改SubscriberMethodFinder 这个类嘛?

GitHubShy avatar Feb 21 '22 01:02 GitHubShy

是的,我就是自定了了一个transform专门用于修改这个类,问题解决了

yumdao avatar Aug 05 '22 07:08 yumdao

是的,我就是自定了了一个transform专门用于修改这个类,问题解决了

请问有没有放到远程的插件直接使用的,不会写这个transform。。

hjqiaho avatar Oct 26 '22 01:10 hjqiaho