如何禁止其他应用在我的Android答题应用上层绘制,或检测此类悬浮应用?
嘿,这个需求太贴合考试类App的防作弊场景了!我之前做过类似的项目,踩过不少坑,来给你梳理几个可行的方案,分主动禁止和被动检测两部分来说,还有针对Cordova的适配建议:
一、主动禁止其他应用的悬浮窗(受控场景优先)
Android系统本身没有给普通App直接禁用其他App悬浮窗权限的API,但有两种途径可以实现类似效果:
引导用户手动关闭全局悬浮窗权限
你可以在App启动/进入答题页时,先检查当前设备是否允许其他App绘制悬浮窗:// 检查当前设备是否有App具备悬浮窗权限 boolean canDrawOverlays = Settings.canDrawOverlays(context);如果返回
true,就弹出清晰的提示,引导用户进入系统设置 -> 应用权限管理 -> 显示在其他应用上层,关闭除系统必要App(比如输入法)之外的所有App的这个权限。
👉 重点:一定要给用户明确的理由(比如“为了保证考试公平,请关闭其他应用的悬浮窗权限”),不然用户大概率会拒绝操作。设备所有者模式(适合企业/定制设备)
如果你的App是部署在受控设备上(比如学校的考试平板、企业的培训设备),可以把App设置为设备所有者,然后通过DevicePolicyManager强制禁止其他App获取SYSTEM_ALERT_WINDOW权限:DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); ComponentName adminComponent = new ComponentName(context, YourDeviceAdminReceiver.class); // 禁止指定第三方App的悬浮窗权限 dpm.setPermissionGrantState(adminComponent, "com.thirdparty.app.package", Manifest.permission.SYSTEM_ALERT_WINDOW, DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);配置设备所有者需要通过ADB命令或者NFC碰传,适合批量部署的场景,普通消费级App不太适用。
二、被动检测悬浮窗应用(普通App首选)
如果没法强制禁止,那检测到悬浮窗后触发提醒/强制退出答题是更现实的方案,Cordova默认的pause/resume或blur/focus事件确实检测不到,得靠原生API,再封装成Cordova插件:
方案1:辅助功能服务(AccessibilityService)
这是最稳定的检测方式,能监听系统中所有窗口的创建/变化。步骤如下:- 创建一个继承自
AccessibilityService的服务,在onAccessibilityEvent里监听窗口事件:@Override public void onAccessibilityEvent(AccessibilityEvent event) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { AccessibilityNodeInfo source = event.getSource(); if (source != null) { int windowType = (int) source.getWindowInfo().getType(); // 适配不同Android版本的悬浮窗类型 boolean isOverlay = false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { isOverlay = windowType == WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { isOverlay = windowType == WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; } if (isOverlay) { // 检测到悬浮窗,触发你的处理逻辑(比如弹窗提醒、记录日志) sendAlertToCordova(); } } } } - 在
AndroidManifest中注册这个服务,声明权限和配置:<service android:name=".OverlayDetectAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service> - 引导用户开启辅助功能:因为辅助功能需要用户手动授权,所以在答题前检查服务是否开启,没开启就跳转到辅助功能设置页面。
👉 把这个服务封装成Cordova插件,就能在JS里监听检测事件了。
- 创建一个继承自
方案2:反射遍历系统窗口(兼容性稍差)
这个方法不需要用户开启辅助功能,但因为用了系统隐藏API,Android 11+可能会被限制,需要测试兼容性:try { Class<?> wmgClass = Class.forName("android.view.WindowManagerGlobal"); Object wmgInstance = wmgClass.getMethod("getInstance").invoke(null); List<?> windows = (List<?>) wmgClass.getMethod("getWindowList").invoke(wmgInstance); for (Object window : windows) { Class<?> windowClass = window.getClass(); int type = (int) windowClass.getMethod("getType").invoke(window); boolean isOverlay = false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { isOverlay = type == WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { isOverlay = type == WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; } if (isOverlay) { // 检测到悬浮窗,返回结果给Cordova return true; } } } catch (Exception e) { e.printStackTrace(); } return false;可以把这个逻辑封装成Cordova插件的一个方法,定期调用(比如每秒检测一次)。
三、Cordova适配注意事项
因为你用的是Cordova,所有原生逻辑都得写成自定义插件:
- 新建一个Cordova插件,把上面的检测/权限逻辑写在插件的Java类里;
- 暴露JS接口,比如
cordova.plugins.OverlayDetector.checkForOverlays()或者监听overlayDetected事件; - 在JS层处理权限引导逻辑,比如弹出提示引导用户开启辅助功能或跳转系统设置。
最后提醒
- Android 10+对悬浮窗的限制更严格,
TYPE_APPLICATION_OVERLAY是唯一合法的悬浮窗类型,旧类型已经废弃,代码里要做好版本兼容; - 没有100%完美的防作弊方案,最好结合多种检测方式(比如辅助功能+窗口遍历),同时配合答题页面的全屏模式、禁止分屏等功能,提升防作弊效果;
- 所有权限申请和引导操作,都要给用户清晰的说明,避免用户反感。




