如何检测Activity上方是否显示系统对话框,避免误触发PiP模式?
解决Android系统对话框触发
onUserLeaveHint导致误进PiP模式的问题 问题本质
onUserLeaveHint()会在用户主动离开Activity(比如按Home键)时触发,但系统级对话框(如USB调试授权、权限申请弹窗)本质是独立的系统Activity,弹出时同样会触发当前Activity的onUserLeaveHint(),导致误触发画中画模式。
可行解决方案
方案1:结合窗口焦点与系统应用判断
在onUserLeaveHint()中先排除Activity自身的配置变更/销毁场景,再判断顶部窗口是否为系统应用,仅在用户主动退后台时触发PiP:
@Override protected void onUserLeaveHint() { super.onUserLeaveHint(); // 排除Activity配置变更(如屏幕旋转)或销毁的情况 if (!isFinishing() && !isChangingConfigurations()) { // 仅当窗口失去焦点且顶部不是系统应用时,才进入PiP if (!getWindow().hasWindowFocus() && !isSystemTopActivity()) { enterPipMode(); } } } // 判断当前顶部Activity是否属于系统应用 private boolean isSystemTopActivity() { ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); if (activityManager == null) return false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // Android 11+ 使用getAppTasks替代废弃的getRunningTasks List<ActivityManager.AppTask> tasks = activityManager.getAppTasks(); if (!tasks.isEmpty()) { ComponentName topComponent = tasks.get(0).getTaskInfo().topActivity; return topComponent != null && isSystemApp(topComponent.getPackageName()); } } else { @SuppressLint("DeprecatedApi") List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1); if (!tasks.isEmpty()) { ComponentName topComponent = tasks.get(0).topActivity; return topComponent != null && isSystemApp(topComponent.getPackageName()); } } return false; } // 辅助方法:判断应用是否为系统应用 private boolean isSystemApp(String packageName) { try { ApplicationInfo info = getPackageManager().getApplicationInfo(packageName, 0); return (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return false; } }
方案2:用焦点状态标记过滤系统对话框
维护一个标记记录窗口是否因系统对话框失去焦点,在onUserLeaveHint()中参考该标记:
private var isFocusLostBySystemDialog = false override fun onWindowFocusChanged(hasFocus: Boolean) { super.onWindowFocusChanged(hasFocus) isFocusLostBySystemDialog = if (!hasFocus) { // 失去焦点时判断顶部是否为系统应用 isSystemTopActivity() } else { false } } override fun onUserLeaveHint() { super.onUserLeaveHint() // 仅当Activity未销毁、非配置变更、且焦点丢失不是系统对话框导致时,进入PiP if (!isFinishing && !isChangingConfigurations && !isFocusLostBySystemDialog) { enterPipMode() } } private fun isSystemTopActivity(): Boolean { val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val tasks = activityManager.appTasks if (tasks.isNotEmpty()) { val topComponent = tasks[0].taskInfo.topActivity topComponent?.let { packageManager.isSystemApp(it.packageName) } ?: false } else false } else { @Suppress("DEPRECATION") val tasks = activityManager.getRunningTasks(1) if (tasks.isNotEmpty()) { val topComponent = tasks[0].topActivity topComponent?.let { packageManager.isSystemApp(it.packageName) } ?: false } else false } } // 扩展PackageManager方法,判断是否为系统应用 fun PackageManager.isSystemApp(packageName: String): Boolean { return try { val info = getApplicationInfo(packageName, 0) (info.flags and ApplicationInfo.FLAG_SYSTEM) != 0 } catch (e: PackageManager.NameNotFoundException) { false } }
方案3:Android 12+ 全局生命周期监听
在Android 12及以上版本,可通过全局Activity生命周期回调,精准判断应用是否真的退到后台:
// 在自定义Application类中注册回调 private int foregroundActivityCount = 0; @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityResumed(@NonNull Activity activity) { foregroundActivityCount++; } @Override public void onActivityPaused(@NonNull Activity activity) { foregroundActivityCount--; } // 其他生命周期方法可省略 }); } public int getForegroundActivityCount() { return foregroundActivityCount; } // 在视频Activity中使用 @Override protected void onUserLeaveHint() { super.onUserLeaveHint(); if (!isFinishing() && !isChangingConfigurations()) { // 仅当应用无前台Activity时,判定为用户主动退后台 if (((MyApplication) getApplication()).getForegroundActivityCount() == 0) { enterPipMode(); } } }
关键注意事项
- 权限适配:Android 10及以下使用
getRunningTasks()需要android.permission.GET_TASKS权限,Android 11+建议用getAppTasks()替代,无需额外权限。 - 系统应用覆盖:通过
ApplicationInfo.FLAG_SYSTEM可覆盖绝大多数系统对话框场景,极少数特殊系统窗口可能需要单独适配。 - 边缘场景处理:需排除Activity配置变更(如旋转)、主动销毁等非用户主动离开的场景,避免误触发。
内容的提问来源于stack exchange,提问作者Radik
相关产品推荐
相关产品推荐

