You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何禁止其他应用在我的Android答题应用上层绘制,或检测此类悬浮应用?

如何禁止其他应用在我的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/resumeblur/focus事件确实检测不到,得靠原生API,再封装成Cordova插件:

  • 方案1:辅助功能服务(AccessibilityService)
    这是最稳定的检测方式,能监听系统中所有窗口的创建/变化。步骤如下:

    1. 创建一个继承自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();
                  }
              }
          }
      }
      
    2. 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>
      
    3. 引导用户开启辅助功能:因为辅助功能需要用户手动授权,所以在答题前检查服务是否开启,没开启就跳转到辅助功能设置页面。
      👉 把这个服务封装成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,所有原生逻辑都得写成自定义插件:

  1. 新建一个Cordova插件,把上面的检测/权限逻辑写在插件的Java类里;
  2. 暴露JS接口,比如cordova.plugins.OverlayDetector.checkForOverlays()或者监听overlayDetected事件;
  3. 在JS层处理权限引导逻辑,比如弹出提示引导用户开启辅助功能或跳转系统设置。

最后提醒

  • Android 10+对悬浮窗的限制更严格,TYPE_APPLICATION_OVERLAY是唯一合法的悬浮窗类型,旧类型已经废弃,代码里要做好版本兼容;
  • 没有100%完美的防作弊方案,最好结合多种检测方式(比如辅助功能+窗口遍历),同时配合答题页面的全屏模式、禁止分屏等功能,提升防作弊效果;
  • 所有权限申请和引导操作,都要给用户清晰的说明,避免用户反感。

火山引擎 最新活动