You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 11及以上无外部存储权限跨设备迁移SQLite数据库方案问询

问题根因

你遇到的Cursor为空的问题不是MediaStore不支持跨设备文件,而是Android作用域存储的固有访问限制,以及你代码中的逻辑缺陷:

  • MediaStore访问限制:从Android 10开始,应用默认只能查询到自身写入MediaStore的文件,其他应用(云盘、系统下载器、文件管理器)存入公共目录的非媒体文件,哪怕文件名完全匹配,你无权限直接查询获取,这和文件是否来自其他设备无关。
  • 代码逻辑缺陷:你调用cursor.moveToNext()前没有判断Cursor是否有数据,一旦查询结果为空就会直接抛出CursorIndexOutOfBoundsException,需要先添加if (cursor != null && cursor.getCount() > 0)的判断逻辑。
合规可行解决方案(均可通过Google上架审核)

方案1:使用系统文件选择器(推荐,适配全版本,无需权限)

这是Google官方推荐的用户主动触发的文件访问方式,完全符合作用域存储规范,不管备份文件来自云盘下载、跨设备传输还是任意目录,只要用户主动选中,应用即可获得读取权限:

  1. 触发系统文件选择器代码:
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);
}
  1. 接收选中的文件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官方自动备份能力:

  1. 在AndroidManifest.xml的application标签添加配置:
<application
    android:allowBackup="true"
    android:fullBackupContent="@xml/backup_rules"
    ...>
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 20:51:01