Android 11及以上无外部存储权限跨设备迁移SQLite数据库方案问询
问题根因
你遇到的Cursor为空的问题不是MediaStore不支持跨设备文件,而是Android作用域存储的固有访问限制,以及你代码中的逻辑缺陷:
- MediaStore访问限制:从Android 10开始,应用默认只能查询到自身写入MediaStore的文件,其他应用(云盘、系统下载器、文件管理器)存入公共目录的非媒体文件,哪怕文件名完全匹配,你无权限直接查询获取,这和文件是否来自其他设备无关。
- 代码逻辑缺陷:你调用
cursor.moveToNext()前没有判断Cursor是否有数据,一旦查询结果为空就会直接抛出CursorIndexOutOfBoundsException,需要先添加if (cursor != null && cursor.getCount() > 0)的判断逻辑。
合规可行解决方案(均可通过Google上架审核)
方案1:使用系统文件选择器(推荐,适配全版本,无需权限)
这是Google官方推荐的用户主动触发的文件访问方式,完全符合作用域存储规范,不管备份文件来自云盘下载、跨设备传输还是任意目录,只要用户主动选中,应用即可获得读取权限:
- 触发系统文件选择器代码:
private static final int REQUEST_CODE_PICK_BACKUP = 1001; private void pickBackupFile() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); // 过滤SQLite数据库文件,可根据实际情况调整MIME类型 intent.setType("application/x-sqlite3"); // 可选:允许用户选择所有类型的文件,避免部分系统识别不了.db的MIME类型 // intent.setType("*/*"); startActivityForResult(intent, REQUEST_CODE_PICK_BACKUP); }
- 接收选中的文件Uri并读取恢复:
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_PICK_BACKUP && resultCode == RESULT_OK && data != null) { Uri backupUri = data.getData(); if (backupUri == null) return; // 以下读取流的逻辑和你原有代码一致,替换原来的MediaStore查询获取Uri的逻辑即可 try { ContentResolver contentResolver = getApplicationContext().getContentResolver(); ParcelFileDescriptor parcelFileDescriptor = contentResolver.openFileDescriptor(backupUri, "r"); FileInputStream fileInputStream = new FileInputStream(parcelFileDescriptor.getFileDescriptor()); File dbFile = new File(getDatabasePath("database.db").getAbsolutePath()); FileOutputStream fileOutputStream = new FileOutputStream(dbFile); byte[] buf = new byte[1024]; int len; while ((len = fileInputStream.read(buf)) > 0) { fileOutputStream.write(buf, 0, len); } fileOutputStream.close(); fileInputStream.close(); parcelFileDescriptor.close(); restartApp(); } catch (IOException e) { e.printStackTrace(); } } }
方案2:使用系统自动备份框架(适合无感知备份恢复场景)
如果不需要用户手动操作备份恢复,可直接使用Android官方自动备份能力:
- 在
AndroidManifest.xml的application标签添加配置:
<application android:allowBackup="true" android:fullBackupContent="@xml/backup_rules" ...>
- 在res/xml目录下创建backup_rules.xml,指定需要备份的数据库文件:
<?xml version="1.0" encoding="utf-8"?> <full-backup-content> <include domain="database" path="database.db" /> </full-backup-content>
配置完成后系统会自动将应用数据备份到用户关联的Google Drive,用户换设备重装应用时会自动恢复数据,不需要你自行处理存储、上传下载逻辑,也无需申请任何权限。
不推荐的方案说明
不要尝试通过申请存储权限强行用MediaStore查询非自身创建的文件:Android 13+对非媒体文件的访问进一步限制,即便申请了READ_MEDIA_*权限也无法查询到.db格式的非媒体文件,完全无法适配高版本系统。
内容的提问来源于stack exchange,提问作者dev.ex
相关产品推荐
相关产品推荐

