Android应用内调用screencap截图返回1生成0B文件解决方案
问题核心原因
screencap 进程返回码1、生成0B文件的根本原因不是存储权限不足,是普通第三方应用进程本身没有读取屏幕帧缓冲区的权限:
- 你声明的
ACCESS_SUPERUSER仅为权限声明标记,不代表应用实际获取了Root授权,普通应用进程直接执行/system/bin/screencap会被SELinux规则拦截,根本拿不到屏幕画面数据。 - 从Android 10开始,系统进一步收紧了非特权进程对系统二进制和帧缓冲区的访问限制,就算申请了全文件访问权限,无Root/无系统签名的应用直接调用screencap必然执行失败。
可行解决方法
根据你的应用权限等级选对应方案即可:
- Root环境下修正screencap调用逻辑
你当前的代码是直接在应用自身进程启动screencap,没有走su提权流程,自然会被权限拦截。正确的提权调用代码参考:
注意:该方案仅在设备已Root、用户给你的应用授予Root权限后生效。fun takeScreenShotWithRoot(badge:String){ thread { val date = Date() val formatDate: CharSequence = DateFormat.format("ddMMyyyyhhmmss", date) val filename = "${badge}_${formatDate}" val targetDir = "${Environment.getExternalStorageDirectory()}/DCIM/Screenshots/" val targetPath = "$targetDir$filename.png" try { // 先提权获取su shell val suProcess = Runtime.getRuntime().exec("su") val shellOs = suProcess.outputStream // 先确保目标目录存在,再执行截图命令,最后退出su进程 val commands = """ mkdir -p $targetDir /system/bin/screencap -p $targetPath exit """.trimIndent() shellOs.write(commands.toByteArray()) shellOs.flush() val exitCode = suProcess.waitFor() shellOs.close() if(exitCode == 0){ Log.d("ScreenshotHDL", "Screenshot saved to $targetPath") }else{ // 调试时记得读取错误流打印具体报错 val errorMsg = suProcess.errorStream.bufferedReader().readText() Log.e("ScreenshotHDL", "Screenshot failed, error: $errorMsg") } }catch (e:Exception){ Log.e("ScreenshotHDL", "Exec error: ${e.message}") } } } - 无Root环境下用官方MediaProjection API实现(Overlay应用推荐)
这是Android 5.0+系统给普通第三方应用开放的唯一合法屏幕采集方式,不需要Root、不需要系统签名,只需要用户一次性授权即可使用:- 通过
MediaProjectionManager.createScreenCaptureIntent()创建授权请求Intent,启动后等待用户同意授权 - 拿到授权结果后获取
MediaProjection实例,创建VirtualDisplay把屏幕内容投射到ImageReader的Surface上 - 从
ImageReader中实时获取Image对象,转成Bitmap后通过MediaStore API存入公共截图目录即可
该方案兼容性最好,不会被系统权限拦截,是目前普通截图类、悬浮窗类应用的主流实现方式。
- 通过
- 系统签名/预装应用场景
如果你的应用是系统预装应用、持有平台签名,可以额外声明android.permission.READ_FRAME_BUFFER权限,声明后直接调用screencap即可正常执行,不需要Root提权。普通第三方应用无法获取该权限。
你当前代码的其他待修正点
- 调试shell命令执行问题时,一定要读取进程的
errorStream打印错误信息,你现在只读了标准输出流,看不到具体的权限拒绝、路径不存在等报错,会大幅增加调试成本。 - 不要直接拼接shell命令字符串执行,路径包含空格、特殊字符时会出现命令解析错误,复杂命令建议用
ProcessBuilder传参数数组的方式调用。 - Android 10+分区存储机制下,不建议直接拼接
/sdcard/硬编码路径写文件,优先使用MediaStore API访问公共目录,避免部分厂商ROM的路径访问限制。
内容的提问来源于stack exchange,提问作者Joan adonys
相关产品推荐
相关产品推荐

