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下载到公共下载目录的备份),调用系统文件选择器获取文件访问权限:
- 先调起系统文件选择器:
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); }
- 处理用户选中的备份文件:
@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
相关产品推荐
相关产品推荐

