You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测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();
        }
    }
}

关键注意事项

  1. 权限适配:Android 10及以下使用getRunningTasks()需要android.permission.GET_TASKS权限,Android 11+建议用getAppTasks()替代,无需额外权限。
  2. 系统应用覆盖:通过ApplicationInfo.FLAG_SYSTEM可覆盖绝大多数系统对话框场景,极少数特殊系统窗口可能需要单独适配。
  3. 边缘场景处理:需排除Activity配置变更(如旋转)、主动销毁等非用户主动离开的场景,避免误触发。

内容的提问来源于stack exchange,提问作者Radik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 04:50:57