如何使用DocumentsProvider暴露应用私有存储目录适配SAF接口
Android SAF 暴露私有目录实现及问题排查方案
一、核心接口实现(仿Termux暴露私有目录逻辑)
你需要自定义继承DocumentsProvider的子类,重点实现以下核心接口:
queryDocument:返回单个文件/目录的元数据- 建立唯一
documentId映射规则:例如根public目录id设为public_root,子文件/目录用相对路径拼接(如public_dir/image/a.jpg),方便后续通过id反向解析本地绝对路径 - 返回的Cursor必须包含以下必填字段:
Document.COLUMN_DOCUMENT_ID、Document.COLUMN_DISPLAY_NAME、Document.COLUMN_MIME_TYPE、Document.COLUMN_SIZE、Document.COLUMN_LAST_MODIFIED、Document.COLUMN_FLAGS - 权限控制(实现仅删除、不可编辑新建需求):
- 目录flag添加
FLAG_SUPPORTS_DELETE,不要添加FLAG_DIR_SUPPORTS_CREATE - 文件flag添加
FLAG_SUPPORTS_DELETE、FLAG_SUPPORTS_READ,不要添加FLAG_SUPPORTS_WRITE、FLAG_SUPPORTS_RENAME等写权限相关flag
- 建立唯一
queryChildDocuments:返回指定目录下的所有子文件/目录元数据,是目录内容展示的核心接口,必须实现- 入参
parentDocumentId解析为本地目录绝对路径 - 遍历该路径下所有文件,将每个文件的元数据按要求组装进Cursor返回
- 入参
openDocument:处理文件读取请求- 根据
documentId解析得到本地文件路径 - 仅允许读模式打开,若接收到写模式请求直接抛出
SecurityException
- 根据
deleteDocument:处理删除请求- 校验待删除文件路径确实在允许暴露的
public目录范围内,避免目录穿越漏洞 - 执行本地文件删除操作
- 校验待删除文件路径确实在允许暴露的
- 所有写相关的可选接口(如
createDocument、renameDocument、copyDocument等)直接不实现,或重写后抛出UnsupportedOperationException即可。
另外不要忘记在AndroidManifest.xml中正确注册你的DocumentsProvider:
<provider android:name=".MyDocumentsProvider" android:authorities="com.luihum.myapp.documents" android:exported="true" android:permission="android.permission.MANAGE_DOCUMENTS"> <intent-filter> <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> </intent-filter> </provider>
二、日志显示文件已纳入但UI不展示排查思路
- 校验Cursor字段完整性:
COLUMN_MIME_TYPE是高频错误点,目录必须填DocumentsContract.Document.MIME_TYPE_DIR,普通文件要填对应正确的MIME类型,类型错误会被系统判定为无效文件不展示 - 检查flag合法性:目录必须添加
FLAG_SUPPORTS_LIST才允许展示子内容,不要混淆目录和文件的专属flag,错误的flag会被系统过滤 - 校验
documentId唯一性:如果多个文件返回了相同的documentId,系统会默认去重,仅展示第一个,导致后续文件丢失 - 检查过滤规则:确认
includeFile中没有隐藏文件过滤、特殊字符过滤等逻辑误杀了需要展示的文件
三、目录可见但子内容不显示解决方案
- 优先校验
queryChildDocuments实现:检查parentDocumentId转本地路径的逻辑是否正确,常见错误是根目录id映射错误,导致遍历了不存在的路径,返回空Cursor - 检查Cursor组装逻辑:确认遍历本地文件后,所有文件的元数据都正确写入了Cursor,没有提前break、列索引对应错误等问题
- 清除SAF缓存:SAF会被系统文件管理器缓存,代码无修改的情况下部分生效大概率是缓存问题。操作路径:卸载重装你的应用 → 找到系统内置的「文件」应用 → 清除存储缓存 → 重新打开SAF界面验证
- 检查调用方权限:如果是第三方应用调用你的SAF提供者,确认启动SAF时添加了
Intent.FLAG_GRANT_READ_URI_PERMISSION临时读权限
内容的提问来源于stack exchange,提问作者luihum
相关产品推荐
相关产品推荐

