Android 11及以上如何创建公共目录存储卸载不丢失的SQLite备份
解决方案
Android 10开始引入分区存储机制,禁止应用直接访问公共目录的原始路径,Android 11进一步强制该规则,原来的Environment.getExternalStoragePublicDirectory方法已废弃,应用专属目录下的文件会随应用卸载删除,要实现卸载不丢失备份,需要通过MediaStore API向公共存储目录(例如下载目录、文档目录)写入文件。
第一步:修改AndroidManifest配置
添加兼容所需的权限和属性:
<!-- 仅Android 9及以下需要存储权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <application ... <!-- 兼容Android 10设备 --> android:requestLegacyExternalStorage="true"> ... </application>
第二步:修改导出数据库的函数代码
替换原有逻辑,Android 10及以上使用MediaStore写入公共下载目录:
private void exportDatabaseNew(LinearLayout linearLayout) { FileInputStream fis = null; OutputStream output = null; try { File dbFile = getDatabasePath(DatabaseHelper.DATABASE_NAME); fis = new FileInputStream(dbFile); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.US); String currentDate = df.format(new Date()); String backupName = "Survey_" + currentDate + ".db"; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10+ 使用MediaStore写入公共下载目录 ContentValues values = new ContentValues(); values.put(MediaStore.MediaColumns.DISPLAY_NAME, backupName); values.put(MediaStore.MediaColumns.MIME_TYPE, "application/x-sqlite3"); values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/Db_Backup"); ContentResolver resolver = getContentResolver(); Uri uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values); if (uri == null) { throw new IOException("Failed to create new MediaStore record"); } output = resolver.openOutputStream(uri); } else { // Android 9及以下沿用原有逻辑 File folder = Environment.getExternalStoragePublicDirectory("Db_Backup"); if (!folder.exists()) { folder.mkdirs(); } File myFile = new File(folder, backupName); output = new FileOutputStream(myFile); } // 写入文件逻辑不变 byte[] buffer = new byte[1024]; int length; while ((length = fis.read(buffer)) > 0) { output.write(buffer, 0, length); } output.flush(); Snackbar.make(linearLayout, R.string.backup_complete, Snackbar.LENGTH_LONG).show(); } catch (Exception e) { e.printStackTrace(); Snackbar.make(linearLayout, "备份失败:" + e.getMessage(), Snackbar.LENGTH_LONG).show(); } finally { // 关闭流 try { if (fis != null) fis.close(); if (output != null) output.close(); } catch (IOException e) { e.printStackTrace(); } } }
注意事项
- 该方案不需要申请
MANAGE_EXTERNAL_STORAGE特殊权限,符合谷歌应用商店的政策要求 - 备份文件会保存在系统下载目录的Db_Backup文件夹下,卸载应用不会被删除
- 如果需要做备份恢复功能,可以通过MediaStore查询下载目录下后缀为.db的备份文件读取即可
- 若出现同名备份文件,系统会自动在文件名后加(1)(2)的后缀避免覆盖,也可以自己先查询同名文件再决定是否覆盖
内容的提问来源于stack exchange,提问作者Aashis Shrestha
相关产品推荐
相关产品推荐

