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

Android 30+系统下应用SQLite数据库替换为Google Drive备份文件问题求解

适配Android 10+分区存储的解决方案

问题根本原因

  • Android 10(API 29)及以上版本正式启用分区存储机制,Environment.getExternalStorageDirectory() 方法已被官方废弃,应用无法直接读写外部存储根目录的文件,你现有代码中读取根目录备份文件的逻辑自然失效。
  • targetSdk=28 时可通过 REQUEST_LEGACY_EXTERNAL_STORAGE 权限绕过分区存储限制,但该适配方案在 targetSdk≥30 时强制失效,必须按官方规则适配分区存储。

推荐适配方案

方案1:备份文件存入应用专属外部存储目录

该目录无需申请任何读写权限,适配成本最低,适合不需要在应用卸载后保留备份的场景:

// 1. 用系统API获取本地数据库路径,不要手动拼接避免路径错误
File currentDB = getDatabasePath(Constants.dataBaseName);
// 2. 备份文件存放在应用专属外部存储目录
File backupDir = getExternalFilesDir(null);
File backupFile = new File(backupDir, Constants.dataBaseName);

try {
    // 复制前必须关闭所有数据库连接,避免文件锁导致复制失败或数据损坏
    // 如果你有全局SQLiteOpenHelper实例,先调用close()关闭
    // dbHelper.close();
    SQLiteDatabase.releaseMemory();

    FileUtils.copyFile(backupFile, currentDB);
    Snackbar.make(findViewById(R.id.activity_backup_restore), getString(R.string.successful_importing_db), Snackbar.LENGTH_LONG).show();
    // 复制完成后重新初始化数据库连接即可正常使用
} catch (IOException e) {
    Snackbar.make(findViewById(R.id.activity_backup_restore), getString(R.string.error_in_importing_db), Snackbar.LENGTH_LONG).show();
}

方案2:使用存储访问框架(SAF)让用户自主选择备份文件

如果需要支持用户选择公共目录下的备份文件(比如从Google Drive下载到公共下载目录的备份),调用系统文件选择器获取文件访问权限:

  1. 先调起系统文件选择器:
private static final int REQUEST_PICK_BACKUP = 1001;

// 触发恢复时调用该方法
private void pickBackupFile() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    // 限制仅显示数据库格式文件,可根据你的备份文件后缀调整
    intent.setType("application/vnd.sqlite3");
    startActivityForResult(intent, REQUEST_PICK_BACKUP);
}
  1. 处理用户选中的备份文件:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_PICK_BACKUP && resultCode == RESULT_OK && data != null) {
        Uri backupUri = data.getData();
        if (backupUri == null) return;

        File currentDB = getDatabasePath(Constants.dataBaseName);
        try (
            InputStream inputStream = getContentResolver().openInputStream(backupUri);
            FileOutputStream outputStream = new FileOutputStream(currentDB)
        ) {
            // 关闭数据库连接
            // dbHelper.close();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, len);
            }
            Snackbar.make(findViewById(R.id.activity_backup_restore), getString(R.string.successful_importing_db), Snackbar.LENGTH_LONG).show();
            // 重新初始化数据库连接
        } catch (IOException e) {
            Snackbar.make(findViewById(R.id.activity_backup_restore), getString(R.string.error_in_importing_db), Snackbar.LENGTH_LONG).show();
        }
    }
}

注意事项

  • 所有数据库文件读写操作前,必须确保所有活跃的数据库连接已关闭,否则会出现文件锁异常或数据损坏。
  • 如果是从Google Drive直接恢复,可直接读取Drive返回的文件流写入本地数据库,无需先下载到本地存储,减少中间环节。

内容的提问来源于stack exchange,提问作者amir hossein kazemi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 00:45:07