Android 14 新 API:直接监听截屏操作,不用再观察媒体文件了~

Android14引入了新的截屏监听API,允许App在用户截屏时接收到通知,但不提供图像数据,旨在保护隐私。开发者可以在特定Activity注册回调,在用户截屏时执行相应操作,如提示用户或处理反馈。不过,此API不监听调试或代码触发的截屏。文章还介绍了如何在App中使用此功能,并强调了其局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

android14-base-screenshot
截屏可以说是手机设备最常用的功能了,Android 系统非常重视截屏方面的体验,近几年的更新都不忘去优化这方面的体验。

从一开始仅在通知栏提醒已截屏,到 Android 11 支持在左下角生成截屏缩略图供编辑或分享,再到 Android 12 支持滚动截屏,以及这次的 Android 14 允许 App 直接监听用户的截屏操作。

为打造更加标准的监听截屏体验,Android 14 正式推出了受隐私保护的相关 API。简单来说,该 API 允许 App 以 Activity 为单位注册监听 Callback,当这些 Activity 可见并且被用户截屏的话,系统将回调这些 Callback 并告知用户当前的 App 监听到了截屏操作。

如下是官方提供的效果示例:

需要留意的是:监听截屏 API 并不提供图像数据,意在告诉 App 截屏的时机,你可以选择在这个时机做相应的操作。比如:客服类 App 可以询问用户是否要上传截屏以反馈问题抑或引导用户从相册选择截屏;隐私程度高的 App 则可以提醒用户不要泄露、做好隐私保护等等。

实战

首先要给 App 声明监听截屏的权限: DETECT_SCREEN_CAPTURE

<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />

这个权限是 normal 级别的,无需动态申请,APK 被安装的时候将自动授予。

Allows an application to get notified when a screen capture of its windows is attempted.

Protection level: normal

之后的写法比较简单,即在需要监听截屏的 Activity 里注册回调:

  1. 创建 ScreenCaptureCallback 的实例,并在 onScreenCapture() 实现里添加所需逻辑,比如此处为弹出警告 Dialog:
    class ScreenShotActivity : AppCompatActivity() {
        private val screenCaptureCallback = ScreenCaptureCallback {
            Log.d("ScreenShot", "onScreenCaptured()", Throwable())
    
            AlertDialog.Builder(this)
                .setMessage("You have taken a screenshot...")
                .setTitle("Warning")
                .show()
        }
        ...
    }
  1. 接着,在该 Activity 的 onStart() 方法里注册上述的 ScreenCaptureCallback 实例,需要指定该回调的执行线程 Executor
class ScreenShotActivity : AppCompatActivity() {
    ...
    override fun onStart() {
        super.onStart()

        Log.d("ScreenShot", "onStart() registerScreenCaptureCallback")
        registerScreenCaptureCallback(mainExecutor, screenCaptureCallback)
    }
    ...
}
  1. 记得在 Activity 的 onStop() 里注销该 ScreenCaptureCallback 实例,避免内存 leak:
class ScreenShotActivity : AppCompatActivity() {
    ...
    override fun onStop() {
        super.onStop()

        Log.d("ScreenShot", "onStop() unregisterScreenCaptureCallback")
        unregisterScreenCaptureCallback(screenCaptureCallback)
    }
    ...
}

而后将目标 Activity 启动,通过 log 可以看到已经注册了监听回调,后面就是操作截屏进行验证。

04-05 21:57:53.904  5230  5230 D ScreenShot: onStart() registerScreenCaptureCallback

可是问题来了:笔者手动没有能够运行 Android 14 的真机,运行 14 的是 Pixel 6 模拟器。

要知道模拟器上你是无法直接同时按下 POWER 键+ VOLUME DOWN 按键来完成截屏的,同时 Pixel ROM 的通知面板也没有提供截屏的 UI 入口。

笔者突然想到了用 adb 模拟按键事件,可苦苦搜寻之后发现 input keyevent 仅支持单个 keyevent 的模拟。就在要放弃的时候,我忽然想到另一种 event 模拟办法,就是 sendevent

首先通过搜索和尝试获悉 POWER 键的长按输入办法:

adb shell sendevent /dev/input/event0 1 116 1 && adb shell sendevent /dev/input/event0 0 0 0 && sleep 3 && adb shell sendevent /dev/input/event0 1 116 0 && adb shell sendevent /dev/input/event0 0 0 0

再找到 VOLUME DOWN 键的长按输入办法:

adb shell sendevent /dev/input/event12 1 114 1 && adb shell sendevent /dev/input/event12 0 0 0 && sleep 3 && adb shell sendevent /dev/input/event12 1 114 0 && adb shell sendevent /dev/input/event12 0 0 0

两者一结合即可模拟同时长按 POWER 键+ VOLUME DOWN 按键的操作:

adb shell sendevent /dev/input/event0 1 116 1 && adb shell sendevent /dev/input/event0 0 0 0 && adb shell sendevent /dev/input/event12 1 114 1 && adb shell sendevent /dev/input/event12 0 0 0 && sleep 3 && adb shell sendevent /dev/input/event0 1 116 0 && adb shell sendevent /dev/input/event0 0 0 0 && adb shell sendevent /dev/input/event12 1 114 0 && adb shell sendevent /dev/input/event12 0 0 0

需要留意的是上述命令需要在 root 环境下执行,即 adb root 之后再执行 sendevent 命令,当然如果你有真机就无需这么迂回啦。

成功模拟用户截屏操作之后,如愿看到了截屏的缩略图和系统在屏幕下方给用户 App 监听了当前截屏的 Toast 提醒

以及系统给予当前 App 的截屏操作的回调:

04-05 21:58:04.459  5230  5230 D ScreenShot: onScreenCaptured()

App 也依据回调弹出了代码里拟定的截屏警告 Dialog。

能监听到 adb 等方式发起的截屏吗?

了解 Android 上截屏的朋友会知道,截屏的发起方式有很多种,除了最常用的按键组合外还可以:

  • 代码中使用 DeviceCapture test 框架、MediaProjectionManager、SurfaceControl 发起
  • 调试中使用 adb 命令发起
  • AS 的 logcat 窗口里的 “Screen Capture” 图标、Emulator 窗口的 “Take Screenshot” 菜单

等等。

那这些场景下的截屏操作,14 的方式是否能监听到呢?

答案是否定的,官方予以了明确说明:

In Android 14, the system API only detects a screenshot if the user performs a specific combination of hardware button presses. The API doesn’t detect screenshots that are taken when running test commands related to screenshots, including ADB, or within instrumentation tests that capture the device’s current screen contents.

笔者也实际尝试了下,无论是 ADB 命令还是 AS 的 UI 入口触发的截屏,都没有回调上述的 ScreenCaptureCallback,系统亦没有截屏的提醒。

理由也可想而知,一般用户不会用这样的方式截屏,这种调试场景下、系统场景下的截屏需求甚至不会在屏幕下方展示缩略图,自然没有必要回调监听的 API 了。

原理

Android 14 的源码尚未公开,但可以打印 ScreenCaptureCallback 的堆栈瞥到相关细节:

04-06 22:35:23.593  4627  4627 D ScreenShot: onScreenCaptured()
04-06 22:35:23.593  4627  4627 D ScreenShot: java.lang.Throwable
04-06 22:35:23.593  4627  4627 D ScreenShot:    at eu.thomaskuenneth.textviewhighlightsdemo.ScreenShotActivity.screenCaptureCallback$lambda-0(ScreenShotActivity.kt:13)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at eu.thomaskuenneth.textviewhighlightsdemo.ScreenShotActivity.$r8$lambda$5lHWf6dEQmh2JMn9HXzbeN068DI(Unknown Source:0)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at eu.thomaskuenneth.textviewhighlightsdemo.ScreenShotActivity$$ExternalSyntheticLambda0.onScreenCaptured(Unknown Source:2)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at android.app.ScreenCaptureCallbackHandler$ScreenCaptureObserver.lambda$onScreenCaptured$0(ScreenCaptureCallbackHandler.java:66)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at android.app.ScreenCaptureCallbackHandler$ScreenCaptureObserver$$ExternalSyntheticLambda0.run(Unknown Source:2)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at android.os.Handler.handleCallback(Handler.java:958)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at android.os.Handler.dispatchMessage(Handler.java:99)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at android.os.Looper.loopOnce(Looper.java:205)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at android.os.Looper.loop(Looper.java:294)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at android.app.ActivityThread.main(ActivityThread.java:8128)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at java.lang.reflect.Method.invoke(Native Method)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)
04-06 22:35:23.593  4627  4627 D ScreenShot:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:946)

通过上述堆栈 ,再结合 13 的现有截屏逻辑,可以推测下该 Callback 的实现:

  1. SystemUI 的 TakeScreenshotService 在截屏完成之后通知 AMS,AMS 判断当前 Activity 的 ScreenCaptureCallbackHandler 中是否存在 ScreenCaptureObserver
  2. YES 则意味着注册了 ScreenCaptureCallback,便通过 AIDL 告知 App 进程,App 进程内部通过 Handler 告知 ScreenCaptureCallbackHandler 在预设的 Executor 执行 onScreenCaptured() 回调

总结

ScreenCaptureCallback 监听截屏 API 的使用简单、清晰,以后不需要再通过监听媒体文件的变化等逻辑来迂回实现,采用官方的方式去探测用户的截屏操作并按照文件读写的规范去提取文件。

但需要留意 adb、代码、AS 等方式发起的截屏无法通过该 API 监听得到,笔者以为这些不属于用户操作,没有必要纳入到监听范围中。另外,这个监听 API 并不是 Application 级别的,每个目标 Activity 都得注册,所以可以考虑在 BaseActivity 中完成 API 的注册和注销。

相信这个 API 后续亦会扩展到 Jetpack 当中,届时无论是否升级到了 Android 14 都可以尝试切换到官方的监听截屏方式中来。

参考

<think>好的,用户现在想了解如何通过命令提示符使用pip安装SymPy库。我需要回顾之前的对话历史,确保回答符合用户的需求,并参考提供的引用内容。 首先,用户之前的问题是关于SymPy中符号变量的定义,比如x = symbols('x')和n的定义。现在用户转向安装SymPy,可能需要具体的步骤指导。根据引用内容,用户提到Anaconda等发行版可能已经包含SymPy,但用户现在想通过pip安装。引用中还提到了安装pipx和python3-venv的方法,但用户明确询问的是使用pip安装,所以需要重点介绍pip的步骤。 接下来,我需要考虑用户可能的操作系统环境,因为不同的系统(如Windows、macOS、Linux)命令可能略有不同。但通常pip是跨平台的,所以可以给出通用步骤,并补充不同系统的注意事项。例如,在Linux/macOS中可能需要使用pip3,而Windows可能直接使用pip。 然后,用户可能遇到权限问题,比如在Linux下安装到系统目录需要sudo,或者建议使用虚拟环境。引用[2]提到了venv虚拟环境,所以应该建议用户创建虚拟环境以避免权限问题,这也符合最佳实践。需要详细说明如何创建和激活虚拟环境,再安装SymPy。 另外,用户可能需要验证安装是否成功,引用[1]中的方法可以引用,即导入sympy并检查版本。这可以作为安装后的验证步骤。同时,用户可能不清楚如何打开命令提示符,可能需要简要说明如何访问终端或CMD。 还需要考虑错误处理,比如pip未安装的情况,如何安装pip,尤其是对于不同的操作系统。例如,在Ubuntu中可能需要使用apt安装python3-pip,而Windows可以通过ensurepip来安装。 最后,确保回答结构清晰,分步骤说明,并包含代码块示例,让用户能够直接复制粘贴命令。同时,生成相关问题,帮助用户深入理解,比如升级库、安装特定版本、使用镜像源等,这些都是常见的后续问题。 总结步骤: 1. 检查Python和pip是否安装。 2. 建议使用虚拟环境(引用[2]的方法)。 3. 安装SymPy的pip命令。 4. 验证安装。 5. 处理常见问题,如权限、镜像源等。 需要确保回答覆盖所有可能的问题点,语言简洁明了,符合中文表达习惯,并且正确使用LaTeX格式,比如变量用$...$包裹,代码块用反引号。同时,引用之前的引用内容,但用户指出之前的引用仅供参考,所以主要依赖当前问题中的引用和用户实际需求。</think>### 通过命令提示符安装SymPy库的完整指南 #### 一、安装前准备 1. **确认Python环境** 在命令提示符中执行以下命令,验证Python和pip是否已安装: ```bash python --version # 查看Python版本(Windows/macOS/Linux通用) pip --version # 查看pip版本 ``` - 若提示`python`命令不存在,尝试使用`python3`(适用于Linux/macOS) - 若未安装pip,需先安装pip(详见第五部分“常见问题”) 2. **建议使用虚拟环境(推荐)** 为避免依赖冲突,优先通过`venv`创建隔离环境(引用自[^2]方法二): ```bash # 创建虚拟环境(项目目录下执行) python -m venv myenv # Windows/Linux/macOS通用 # 激活环境 source myenv/bin/activate # Linux/macOS myenv\Scripts\activate # Windows ``` #### 二、安装SymPy的核心步骤 1. **通过pip安装** 在激活的虚拟环境或系统环境中执行: ```bash pip install sympy # 通用命令 ``` - 若需指定版本(如安装1.12版): ```bash pip install sympy==1.12 ``` 2. **验证安装结果** 通过Python交互模式检查(引用自[^1]): ```python >>> import sympy >>> sympy.__version__ # 输出示例:'1.12' ``` #### 三、操作系统适配说明 | 操作系统 | 关键命令差异 | 典型问题解决 | |----------|-------------------------------------|-------------------------------| | Windows | 使用`python`而非`python3` | 路径包含空格时用`"`包裹路径 | | Linux | 可能需要`pip3`和`sudo`权限 | `sudo apt install python3-pip` | | macOS | 系统自带Python2,建议通过`brew`安装Python3 | 使用`python3 -m pip install sympy` | #### 四、安装流程示意图 ```mermaid graph TD A[启动命令提示符] --> B{是否使用虚拟环境?} B -->|是| C[创建并激活venv] B -->|否| D[直接安装到系统环境] C --> E[执行pip install sympy] D --> E E --> F[验证导入和版本] ``` #### 五、常见问题处理 1. **权限不足导致的安装失败** ```bash # Linux/macOS系统级安装(不推荐) sudo pip install sympy # 更安全的替代方案:添加--user参数 pip install --user sympy ``` 2. **网络连接问题** 使用国内镜像源加速下载(如清华大学源): ```bash pip install sympy -i https://pypi.tuna.tsinghua.edu.cn/simple ``` 3. **pip未安装的修复方法** ```bash # Linux (Debian/Ubuntu) sudo apt update && sudo apt install python3-pip # Windows(通过Python内置模块) python -m ensurepip --upgrade ``` #### 六、卸载与升级 1. **卸载SymPy** ```bash pip uninstall sympy ``` 2. **升级到最版** ```bash pip install --upgrade sympy ``` ### 相关问题 1. 如何在不同Python版本中管理SymPy的安装? 2. 使用conda安装SymPy与pip安装有何区别? 3. 如何通过Docker容器部署SymPy环境? [^1]: 版本验证方法参考SymPy官方文档 [^2]: 虚拟环境创建方法符合Python PEP 405标准
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TechMerger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值