Tencent / Shadow

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

插件APP切换到后台后再切换回前台,插件和宿主之间插入其他应用的层级关系,导致插件APK无法退回到宿主APK #1199

Closed LeonWu6 closed 1 year ago

LeonWu6 commented 1 year ago

在 shadow 的 sample APP 加载插件后,显示的UI是:com/tencent/shadow/sample/plugin/app/lib/gallery/cases/UseCaseSummaryFragment.java

这时,按手机的 Home 键,将 sample APP 切换到后台, 然后再通过 android 的近期任务栏将 sample APP 再切换回前台。 这时通过 adb shell dumpsys activity containers > activity_containers_shadow.txt 指令查看当前容器层级信息,发现 sample 的宿主和插件之间不会加入其他容器层信息。 层级关系如下:插件activity --> 宿主 activity

_#1 ActivityRecord{97b453f u0 com.tencent.shadow.sample.host/com.tencent.shadow.sample.plugin.runtime.PluginDefaultProxyActivity t12} type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]

0 815fc2f com.tencent.shadow.sample.host/com.tencent.shadow.sample.plugin.runtime.PluginDefaultProxyActivity type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]

     #0 ActivityRecord{c76a15d u0 com.tencent.shadow.sample.host/.MainActivity t12} type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]
      #0 bdb731d com.tencent.shadow.sample.host/com.tencent.shadow.sample.host.MainActivity type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]_

而我的一个使用Shadow 框架的 APK,将插件apk加载起来后,再按Home键切到后台, 然后再通过近期任务栏将插件APK再切换回到前台。 这时,同样通过 adb shell dumpsys activity containers > activity_containers_em.txt 指令查看当前容器层级信息,发现宿主和插件之间会加入了 com.android.launcher 的层次关系。 层级关系如下:插件activity --> com.android.launcher --> 宿主 activity

_#0 ActivityRecord{4130f91 u0 com.oplus.em/.runtime.PluginDefaultProxyActivity t8} type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]

0 e30a018 com.oplus.em/com.oplus.em.runtime.PluginDefaultProxyActivity type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]

    #3 Task=1 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]
     #0 Task=2 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]
      #0 ActivityRecord{3388b3f u0 com.android.launcher/.Launcher t2} type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]
       #0 68e6d0c com.android.launcher/com.android.launcher.Launcher type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]
    #2 Task=7 type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]
     #2 ActivityRecord{f3628ee u0 com.oplus.em/.MainActivity t7} type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]
      #0 1cd801a com.oplus.em/com.oplus.em.MainActivity type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2412]_

这个问题,使得我这个 apk 在切换到后台再切换回前台操作后,无法从插件apk的 activity 回到宿主 apk 的activity 中,而是会根据窗口容器层级树,直接从插件 apk 回到 launcher UI。

请教: 为什么Shadow sample app 可以在切换回后台再切回前台过程中,容器层级树上,插件app和宿主app之间不会插入 com.android.launcher ?是有哪些关键点被我忽略了吗? 请您指点迷津,十分感谢!

shifujun commented 1 year ago

这个问题只和Activity栈有关。Shadow在这个问题上特别简单,没有任何特殊处理。所以你需要关注的点就是PluginDefaultProxyActivity 等插件在宿主中的壳子Activity在manifest中声明的各种属性,比如singleTask等。对于系统来说,它只能看看是宿主启动了这个壳子Activity,即使相同的壳子配对了不同的插件activity。

查这个问题时你应该注意下切到后台前后Activity栈中的对象是否一致,也可能是重建的。还应该关注activity manager系统日志。另外我没什么见过launcher出现在栈里的印象。

没有代码就只能猜到这了。

LeonWu6 commented 1 year ago

Hi shifujun @shifujun

感谢您的回复! 这个问题的原因我找到了。

我在一个APK(后面称为ParentAPK)中通过 startActivity() 的方式,启动了我的 com.oplus.em 宿主 APP的 MainActivity,但我没有加 Intent.FLAG_ACTIVITY_NEW_TASK 这个 flag,使得 com.oplus.em/.MainActivity 这个宿主APP的 activity 是在 ParentAPK 的task 里面,如下:

_Task{7219a54 #92 type=standard A=1000:com.oplus.parentApk U=0 visible=true visibleRequested=true mode=fullscreen translucent=false sz=3} mLastPausedActivity: ActivityRecord{aee6e1 u0 com.oplus.em/.pluginhelper.PluginLoadActivity t-1 f}} mLastNonFullscreenBounds=Rect(283, 690 - 797, 1770) isSleeping=false

而我的 com.oplus.em 的插件 Activity 是在自己的 com.oplus.em 的 task 里面。如下:

_* Task{9bd32b5 #95 type=standard A=1000:com.oplus.em U=0 visible=true visibleRequested=true mode=fullscreen translucent=true sz=1} mLastNonFullscreenBounds=Rect(283, 690 - 797, 1770) isSleeping=false topResumedActivity=ActivityRecord{4c706c0 u0 com.oplus.em /.runtime.PluginDefaultProxyActivity t95}

所以,宿主APP 和插件 APP是在不同的两个 APP task 中。 所以,当插件APP从前台切换到后台,再切回前台时,插件 task和 宿主task 中间就插入了 launcher UI 的 task。

解决方法就是在 parentApk 启动我的 com.oplus.em 宿主APP 的 MainActivity 时,加上一个 flag: intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 这样,com.oplus.em 的宿主和插件就在相同的 com.oplus.em 的task 中了,中间不会再插入其他 task。

itxiaox commented 1 year ago

我也碰到类似问题,宿主Activity通过shadow打开插件Activity后,关闭插件Activity,直接返回到桌面,并不是期望的返回到上个Activity, 即宿主Actvity; 经过实验发现比较奇怪的是,当我的插件apk时候一个简单的APK(demo.apk)的时候是符合预期的,能正常返回上个Activity, 但当我把插件apk换成实际复杂业务插件APK的时候就出现上面情况,有大佬碰到过类似情况吗