【免费下载链接】CircleToSearch
CircleToSearch with multi search engine support for all android
CircleToSearch是一款面向所有 Android 设备的多引擎识图搜索应用(Google、Bing、Yandex、TinEye)。它的核心技术,就是借助 Android 11+(API 30)为无障碍服务提供的takeScreenshot API,在不申请 MediaProjection、不弹系统授权对话框的情况下截取当前屏幕,再对截屏结果中的 HardwareBuffer 进行包装与解析。本文将结合这个开源项目的源码,完整拆解这条从"无权限截屏"到"软件位图"的链路。
为什么选 takeScreenshot:无权限截屏的关键
Android 应用截屏传统上有两条路,体验都不好:
| 方案 | 痛点 |
|---|---|
| MediaProjection 虚拟屏 | 每次使用都要弹"是否允许录屏"对话框,且流程繁琐 |
| 读系统截屏文件 | Android 10+ 存储隔离,基本不可行 |
而 Android 11 引入的AccessibilityService.takeScreenshot()让已开启的无障碍服务可以直接抓取指定 Display 的当前画面:
- ✅ 无需
MediaProjection权限、无系统弹窗,截屏"零打断" - ⚠️ 前提是用户手动开启无障碍服务(这本身就是系统级授权),且服务需声明
canTakeScreenshot="true"
这也是 CircleToSearch 能"在任意应用之上随手圈选搜索"的底层能力——截屏必须发生在 Overlay 弹出之前,否则截到的就是自己的界面。
前置条件:无障碍服务配置中开启截屏能力
整个能力的第一块拼图在 accessibility_service_config.xml 中:
<accessibility-service android:canRetrieveWindowContent="true" android:canPerformGestures="true" android:canTakeScreenshot="true" ... />同时 AndroidManifest.xml 中服务需绑定BIND_ACCESSIBILITY_SERVICE权限。另外项目在onServiceConnected中还会叠加FLAG_RETRIEVE_INTERACTIVE_WINDOWS等标志(见 CircleToSearchAccessibilityService.kt),用于读取多窗口内容,保证截屏与文本节点坐标对齐。
takeScreenshot 实战:从调用到回调
核心实现在 performCapture(),骨架如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { takeScreenshot( Display.DEFAULT_DISPLAY, // 目标显示 executor, // 回调执行的线程池 object : TakeScreenshotCallback { override fun onSuccess(screenshot: ScreenshotResult) { /* 处理 HardwareBuffer */ } override fun onFailure(errorCode: Int) { /* 记录错误码 */ } } ) }三个要点:
- 版本门槛是 API 30(R),低版本需走其他触发通道或降级;
- 回调在指定 Executor 上执行(项目用单线程 Executor),不是主线程,后续拉起 Activity 前注意线程切换;
- 成功回调拿到的是
ScreenshotResult,它不直接给 Bitmap,而是携带一块HardwareBuffer+ColorSpace——这正是下一节要解析的部分。
HardwareBuffer 解析:wrapHardwareBuffer 与 ARGB_8888 拷贝
ScreenshotResult.hardwareBuffer是 GPU 侧的显存块,不能直接当普通 Bitmap 裁剪、读取像素。项目采用"包装 → 拷贝 → 释放"三步曲(源码位置):
val hardwareBuffer = screenshot.hardwareBuffer val colorSpace = screenshot.colorSpace // ① 硬件包装:得到一个 GPU 支撑的(不可变)Bitmap val bitmap = Bitmap.wrapHardwareBuffer(hardwareBuffer, colorSpace) if (bitmap == null) { hardwareBuffer.close() // 包装失败,立即释放缓冲 return } // ② 拷贝为纯软件位图,可裁剪、可逐像素访问 val copy = bitmap.copy(Bitmap.Config.ARGB_8888, false) hardwareBuffer.close() // ③ 拷贝完成,显式关闭 HardwareBuffer BitmapRepository.setScreenshot(copy) launchOverlay(searchModeOverride)为什么要传 ColorSpace?高分辨率屏幕常使用广色域(如 Display P3),包装时带上原始 ColorSpace 可以避免颜色偏移。
为什么要再 copy 一次?wrapHardwareBuffer返回的 Bitmap 是硬件支撑的(hardware-backed),对它的读写受 GPU 限制且不可修改;copy(ARGB_8888, false)生成纯 CPU 位图后,后续的Bitmap.createBitmap()裁剪、OCR 像素读取才能正常工作。
必须close()吗?必须。HardwareBuffer实现了ExplicitCloseable,忘记关闭会泄漏显存,重复 close 也会抛异常——所以项目里"包装失败"与"拷贝完成"两条路径都显式调用了close()。
内存级接力:BitmapRepository 的无落盘设计
截屏完成后位图并不通过 Intent 传递(Bitmap 太大,parceling 有 1MB 上下限风险),而是存进一个内存单例 BitmapRepository.kt:
object BitmapRepository { private var screenshot: Bitmap? = null fun setScreenshot(bitmap: Bitmap?) { screenshot = bitmap } fun getScreenshot(): Bitmap? = screenshot fun clear() { screenshot = null } }接力链非常简洁:
- 无障碍服务
setScreenshot(copy)写入; - OverlayActivity.loadScreenshot() 启动后读取,渲染出圈选界面;
- 关闭 Overlay 时
BitmapRepository.clear(),onDestroy 再做兜底清理。
这个设计的隐私收益很直观:截屏全程只在内存中流转,从不落盘;即使用户"保存选区",写入相册的也只是用户圈定的裁剪区域(见 ImageUtils.kt 的cropBitmap)。整体架构可参考官方文档 docs/image_search.md。
多条触发路径:一次截屏逻辑,多种入口
同一个performCapture()被多个入口复用,用户按习惯选择即可:
- ⚡快速设置磁贴:下拉通知栏点击 CTS 磁贴(CircleToSearchTileService.kt)
- 🗣️设为默认助手:长按 Home 键触发,此时系统直接回调
onHandleScreenshot(Bitmap),截屏由系统完成,AssistSessionService 只需存入 Repository 并拉起 Overlay - 🔘状态栏双击 / 悬浮气泡:由无障碍服务自行调用
takeScreenshot
多条路径最终都收敛到BitmapRepository → OverlayActivity这一条主干,新增触发方式时几乎不用改动图像管线。
踩坑清单:4 个最容易翻车的地方
- 忘记
hardwareBuffer.close()—— 显存泄漏,建议放在 finally 或两条分支都显式调用; - 直接在硬件 Bitmap 上裁剪——
wrapHardwareBuffer结果不可写、不可部分裁剪,务必先copy(ARGB_8888, false); - 线程假设错误——
TakeScreenshotCallback在 Executor 上回调,不要默认它在主线程; - 忽略
onFailure—— 系统繁忙、多屏等场景可能失败,至少记录errorCode并给用户提示。
总结
CircleToSearch 用不到 50 行代码演示了 takeScreenshot API 的完整范式:声明canTakeScreenshot→ 回调取 HardwareBuffer →wrapHardwareBuffer包装 → 拷贝为 ARGB_8888 → 内存单例接力 → 用完即清。它证明了在 Android 上,"无权限、无打断"的截屏能力并不需要任何灰色手段——只要你的应用场景符合无障碍服务的定位,这是官方给的正经通道。如果你想在自己的无障碍类应用(屏幕翻译、内容提取、辅助搜索等)中实现类似能力,这套链路可以直接照搬。
【免费下载链接】CircleToSearch
CircleToSearch with multi search engine support for all android
相关推荐
揭秘Android截屏限制:FLAG_SECURE深度解析与实战解决方案
揭秘Android截屏限制:FLAG_SECURE深度解析与实战解决方案 在Android生态系统中,FLAG_SECURE安全标志如同无形的屏障,影响着用户对
移动开发DisableFlagSecure:彻底解放Android截屏权限的终极工具
DisableFlagSecure:彻底解放Android截屏权限的终极工具 你是否曾遇到过这样的困扰:想要保存银行应用的交易记录、记录游戏精彩瞬间或备份重要文
移动开发Android FLAG_SECURE限制突破:全面解锁截屏权限的终极方案
Android FLAG_SECURE限制突破:全面解锁截屏权限的终极方案 在移动应用安全防护体系中,FLAG_SECURE标志作为Android系统的重要安全
移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考