Tencent / Shadow

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

解绑 PPS(PluginProcessService) 之后,再次绑定会触发异常(PPS出现多实例) #1243

Closed octopuslaowang closed 2 months ago

octopuslaowang commented 9 months ago

BasePluginProcessService.java

    /**
     * PPS应该代表插件进程的生命周期。插件进程应该由PPS启动而启动。
     * 所以不应该出现在同一个插件进程有两个PPS对象的情况。
     * 如果出现,将会重复加载Loader、Runtime、业务等插件,进而出现非常奇怪的异常。
     * 因此,用这样一个静态变量检测出这种情况。PPS不能死后重新创建。需要在上层合理设计保持PPS始终存活。
     */
    private static Object sSingleInstanceFlag = null;

    @Override
    public void onCreate() {
        if (sSingleInstanceFlag == null) {
            sSingleInstanceFlag = new Object();
        } else {
            throw new IllegalStateException("PPS出现多实例");
        }
        super.onCreate();
        if (mLogger.isInfoEnabled()) {
            mLogger.info("onCreate:" + this);
        }
    }

BasePluginProcessService 中 sSingleInstanceFlag 用来保证 PPS 单例,当同一个进程再次创建 PPS 时会在 onCreate 中会触发异常(PPS出现多实例)

目前问题: 当主动杀进程时,需要主动去解绑 PPS (否则系统框架在进程被杀后会重启 PPS,并且重启进程)。如果在解绑 PPS 后(onDestroy 之后),进程还没被杀之前,立即又触发了插件加载,需要重新绑定并创建 PPS ,此时会触发 onCreate 中的异常

    @Override
    public void onDestroy() {
        super.onDestroy();
        sSingleInstanceFlag = null;
        if (mLogger.isInfoEnabled()) {
            mLogger.info("onDestroy:" + this);
        }
    }

目前解法: 在 onDestroy 中添加 sSingleInstanceFlag = null;

主要想咨询的问题是,不知道目前这样的解法,会不会对 Shadow 框架产生其他的影响

麻烦有空帮忙看一下,感谢!

shifujun commented 9 months ago

首先不管是什么问题,什么原因,赋null值的做法是错的。因为你观察这个flag的作用,它仅仅是帮我们检测出一种情况,及早的报错出来,避免出现难以定位的更多bug。就是那段注释的意思。所以赋null还不如把这个flag删了呢。

或者说你描述的场景就是这个flag需要检测出的一种情况。

这段代码确实没有给出更优雅的结束PPS的实现。而仅仅是依赖上层调用代码不要搞出多实例的复杂场景。所以这里只保证如果PPS是单例的,而且这个进程就是PPS触发创建的,就是这样理想的情况,这样的话,PPS才能如我们所想的工作正常。

如果不解绑PPS,PPS进程被杀掉了,然后系统重启了PPS。那这个PPS应该也是可以正常用的呀。主进程只需要正常查询它已经加载什么插件了就行了吧?

octopuslaowang commented 9 months ago

首先不管是什么问题,什么原因,赋null值的做法是错的。因为你观察这个flag的作用,它仅仅是帮我们检测出一种情况,及早的报错出来,避免出现难以定位的更多bug。就是那段注释的意思。所以赋null还不如把这个flag删了呢。

或者说你描述的场景就是这个flag需要检测出的一种情况。

这段代码确实没有给出更优雅的结束PPS的实现。而仅仅是依赖上层调用代码不要搞出多实例的复杂场景。所以这里只保证如果PPS是单例的,而且这个进程就是PPS触发创建的,就是这样理想的情况,这样的话,PPS才能如我们所想的工作正常。

如果不解绑PPS,PPS进程被杀掉了,然后系统重启了PPS。那这个PPS应该也是可以正常用的呀。主进程只需要正常查询它已经加载什么插件了就行了吧?

非常感谢耐心回复! 但是还有几点补充和疑惑,麻烦有空再帮忙解答一下。

目前我的业务场景有个前提是,必须主动关闭 PPS 进程。 基于这个前提,只能去解绑 PPS,所以才会发生这样的问题(解绑 PPS 后,进程还没被杀之前,又创建 PPS 的异常)。 当然解法可以有很多种,我的解法的确破坏了 sSingleInstanceFlag 设计的初衷。

看了你的回复,我的理解是

  1. 我目前的解法会导致 PPS 无法正常工作,是这样吗?如果是的,能尽量具体说明是哪些影响吗?
  2. PPS 和 PPS 进程应该唯一绑定。如果加载插件时,PPS 已经解绑,应该创建新的 PPS 进程,是这样吗?