Android PictureInPicture模式Activity销毁重建及Fragment UI不显示问题
问题原因分析
1. 点击画中画X按钮触发Activity销毁重建的原因
- 配置变更声明不全:画中画切换会触发屏幕方向、最小屏幕尺寸、UI模式等配置变更,你的Manifest中
configChanges没有声明这三类变更,系统默认会销毁重建Activity来适配配置变化,从你进入画中画时的日志就能看到已经发生了一次重建,点击关闭按钮时的配置变更会再次触发该逻辑。 - Activity启动模式和任务栈配置冲突:你给RoomActivity设置了
singleTask启动模式、独立的taskAffinity以及excludeFromRecents=true,点击画中画关闭按钮时系统默认会finish当前Activity实例,但前台服务还在运行的状态下,系统检测到该任务栈没有可回退的页面,会触发Activity自动重建来留存任务栈。 - 旧版Fragment标签的问题:你在布局中使用了已经不推荐的
<fragment>标签来承载NavHostFragment,该标签在宿主Activity重建时会自动重新创建内部的Fragment实例,不会复用原有状态,加重了重建问题。
2. 通知打开后UI不显示的原因
点击X按钮后Activity异常重建时,Fragment的状态恢复出现异常,后续通过通知启动已经处于后台的Activity时,系统直接复用了异常状态的Fragment实例,没有触发onViewCreated生命周期,导致该方法中注册的观察者没有生效,UI无法更新。
解决方案
步骤1:补全configChanges配置,避免配置变更触发重建
在Manifest的RoomActivity的configChanges属性中添加画中画相关的配置变更声明:
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|screenSize|fontScale|orientation|smallestScreenSize|uiMode"
添加后画中画切换带来的配置变更会由Activity自行处理,不会触发系统销毁重建逻辑。
步骤2:拦截画中画关闭事件,主动结束Activity避免异常重建
在RoomActivity中重写画中画模式变化和关闭请求的回调,点击关闭按钮时主动结束Activity,阻止系统自动重建:
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) { super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig) // 退出画中画且没有前台展示需求时,直接关闭Activity if (!isInPictureInPictureMode) { finish() } } // Android 12及以上专门处理画中画关闭按钮点击 @RequiresApi(Build.VERSION_CODES.S) override fun onCloseRequested() { super.onCloseRequested() finish() }
步骤3:替换Fragment容器为官方推荐实现
把room_activity.xml中的<fragment>标签替换为FragmentContainerView,优化Fragment状态恢复逻辑:
<androidx.fragment.app.FragmentContainerView android:id="@+id/roomHostFragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/room_nav_graph" />
步骤4:调整PendingIntent配置,保证Activity启动状态正确
修改PendingIntent的Flag,匹配singleTask启动模式,同时保证参数可以正常更新:
private fun getPendingIntent(bundle: Bundle): PendingIntent = Intent(this@WebRtcVideoService, RoomActivity::class.java).let { it.putExtras(bundle) it.flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP val flag = when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> PendingIntent.FLAG_UPDATE_CURRENT else -> PendingIntent.FLAG_UPDATE_CURRENT } PendingIntent.getActivity(this@WebRtcVideoService, 0, it, flag) }
步骤5:优化观察者注册逻辑,兼容场景边缘case
将Fragment的观察者注册逻辑绑定到视图生命周期,就算Fragment复用也能正常收到回调:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { uiState -> // 处理UI更新逻辑 } } } }
内容的提问来源于stack exchange,提问作者james04
相关产品推荐
相关产品推荐

