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

Android无需申请存储权限检测应用截图的可行方案问询

问题

我需要检测本应用被截图的事件,但无需获取截图元数据或实际图片引用。当前的实现方式是自定义ContentObserver并订阅MediaStore.Images.Media.EXTERNAL_CONTENT_URI,代码如下:

mContentResolver.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, this)

对应的onChange回调实现:

@Override
synchronized public void onChange(boolean selfChange, Uri uri) {
    super.onChange(selfChange, uri);
    if (uri.toString().startsWith(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString())) {
        try {
            checkPermissionAndProcess(uri);
        } catch (Exception e) {
            Timber.w(e, "onChange error : %s", e.toString());
        }
    } else {
        Timber.v("[Finish] not EXTERNAL_CONTENT_URI ");
    }
}

该方案需要申请Manifest.permission.READ_EXTERNAL_STORAGE权限,授权后通过路径匹配判断是否为截图:

private boolean matchPath(String path) {
    return (path.toLowerCase().contains("screenshots/") && !path.contains(FILE_POSTFIX));
}

但这种方式会被外部存储的任何图片变更触发回调,且必须申请存储权限,请问有没有无需申请存储权限的替代方案?

无需存储权限的截图检测方案

以下是几种不需要申请READ_EXTERNAL_STORAGE权限的可行方案:

1. 利用帧监听API(Android 7.0+)

Android 7.0(API 24)及以上提供了Window.OnFrameMetricsAvailableListener,可以监听屏幕帧的捕获标记——截图操作会触发系统标记当前帧为"已被捕获",以此来判断截图事件。

实现代码示例:

private Window.OnFrameMetricsAvailableListener frameListener;

@Override
protected void onResume() {
    super.onResume();
    frameListener = (window, frameMetrics, dropCount) -> {
        int flags = frameMetrics.getMetric(FrameMetrics.FRAME_METRICS_FLAGS);
        if ((flags & FrameMetrics.FRAME_METRIC_FLAG_WINDOW_CAPTURED) != 0) {
            // 检测到截图事件,执行你的逻辑
            handleScreenshotDetected();
        }
    };
    getWindow().addOnFrameMetricsAvailableListener(frameListener, null);
}

@Override
protected void onPause() {
    super.onPause();
    if (frameListener != null) {
        getWindow().removeOnFrameMetricsAvailableListener(frameListener);
    }
}

private void handleScreenshotDetected() {
    // 这里写截图触发后的处理逻辑
}

注意:部分定制ROM可能存在兼容性差异,需要多设备测试验证。

2. 监听厂商私有广播(Android 10+ 有限兼容)

不少主流厂商(小米、华为等)会在截图时发送私有系统广播,无需存储权限即可监听,但该方案依赖厂商实现,没有统一标准。

示例代码(以小米为例):

private BroadcastReceiver screenshotReceiver;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    screenshotReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if ("android.intent.action.SCREENSHOT".equals(intent.getAction())) {
                handleScreenshotDetected();
            }
        }
    };
    IntentFilter filter = new IntentFilter();
    filter.addAction("android.intent.action.SCREENSHOT");
    // 可追加其他厂商的广播Action,比如华为的"com.huawei.android.intent.action.SCREENSHOT"
    registerReceiver(screenshotReceiver, filter);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (screenshotReceiver != null) {
        unregisterReceiver(screenshotReceiver);
    }
}

3. 无障碍服务监听(全版本兼容但需用户授权)

通过无障碍服务可以监听系统UI变化,当截图发生时,系统会弹出预览窗口或发送通知,无障碍服务可捕获这些事件。

实现步骤:

  1. 创建继承AccessibilityService的服务类
  2. 在onAccessibilityEvent中识别截图相关的窗口/通知
  3. 引导用户手动开启无障碍权限(属于敏感权限,需明确告知用户用途)

示例代码:

public class ScreenshotAccessibilityService extends AccessibilityService {
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            CharSequence className = event.getClassName();
            // 匹配小米的截图预览窗口类名,其他厂商需自行适配
            if ("com.miui.screenshot.ScreenshotPreviewActivity".equals(className)) {
                // 发送自定义广播通知主应用
                Intent intent = new Intent("com.your.app.SCREENSHOT_DETECTED");
                sendBroadcast(intent);
            }
        }
    }

    @Override
    public void onInterrupt() {}
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 21:25:24