Android 10系统删除文件/文件夹后无法使用相同名称创建新文件问题
问题根因
Android 10及以上版本引入的MediaStore机制存在以下特性,是导致你问题的核心原因:
- 手动删除文件/文件夹后,MediaStore数据库不会立即清除对应记录,只会标记为
IS_TRASHED=1(回收站状态)或残留未清理的脏数据,此时插入同名文件会因为唯一索引冲突返回null - 你当前使用
MediaStore.MediaColumns.DATA列查询记录的方式已被Android官方废弃,Android 11之后该列仅返回无效空值,导致你无法查询到残留的脏记录,误以为路径无占用
修复方案
1. 修复文件存在性查询逻辑
废弃DATA列,改用RELATIVE_PATH + DISPLAY_NAME作为查询条件,同时开启回收站记录查询权限,确保能查到所有残留的脏记录:
public static Uri isFileExist(String relativePath, String fileName) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { // 低于Android 10版本走原有逻辑即可 return null; } Uri queryUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI.buildUpon() // 允许查询回收站中的记录 .appendQueryParameter(MediaStore.QUERY_ARG_MATCH_TRASHED, "true") .build(); String[] projection = new String[]{MediaStore.MediaColumns._ID}; String selection = MediaStore.MediaColumns.RELATIVE_PATH + "=? AND " + MediaStore.MediaColumns.DISPLAY_NAME + "=?"; String[] selectionArgs = new String[]{relativePath, fileName}; try (Cursor cursor = getContext().getContentResolver().query(queryUri, projection, selection, selectionArgs, null)) { if (cursor != null && cursor.moveToFirst()) { long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)); return ContentUris.withAppendedId(MediaStore.Downloads.EXTERNAL_CONTENT_URI, id); } } return null; }
2. 新增冲突后重试逻辑
插入返回null时,先删除残留的脏记录再重试插入:
public static void createFile() throws IOException { String contentType = "text/log"; Date cDate = new Date(System.currentTimeMillis()); String today = new SimpleDateFormat("yyyy_MM_dd").format(cDate); long seconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()); String relativePath = Environment.DIRECTORY_DOWNLOADS + File.separator + "MyApp Log" + File.separator; String fileName = today + ".txt"; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ContentValues contentValues = new ContentValues(); contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName); contentValues.put(MediaStore.MediaColumns.MIME_TYPE, contentType); contentValues.put(MediaStore.MediaColumns.DATE_ADDED, seconds); contentValues.put(MediaStore.MediaColumns.DATE_MODIFIED, seconds); contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1); contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath); ContentResolver resolver = getContext().getContentResolver(); Uri outputUri = resolver.insert(getDownloadUri(), contentValues); // 插入失败时先删残留记录再重试 if (outputUri == null) { Uri existUri = isFileExist(relativePath, fileName); if (existUri != null) { // 删除残留的脏记录 resolver.delete(existUri, null, null); // 重试插入 outputUri = resolver.insert(getDownloadUri(), contentValues); } if (outputUri == null) { throw new IOException("Failed to create new MediaStore record."); } } try (final OutputStream stream = resolver.openOutputStream(outputUri)) { if (stream == null) return; // 写入日志内容的逻辑写在这里 } finally { ContentValues updateValues = new ContentValues(); updateValues.put(MediaStore.MediaColumns.IS_PENDING, 0); resolver.update(outputUri, updateValues, null, null); } } }
额外注意点
- RELATIVE_PATH末尾请加上
File.separator,避免部分系统识别路径错误 - 如果应用需要支持覆盖当天已存在的日志文件,可以在插入前主动查询是否存在记录,存在直接打开写入即可,无需反复创建新文件
内容的提问来源于stack exchange,提问作者propoLis
相关产品推荐
相关产品推荐

