Android Studio 2022版本下Room数据库正确备份与恢复方法(Kotlin/Java实现)
Hey there! I totally get it—older tutorials often fall flat with newer Android Studio and Room versions, especially since 2022 brought key changes to how databases handle write operations. Let’s break down the up-to-date, reliable way to back up and restore your Room database, fixing those outdated mistakes once and for all.
Key Background (Why Old Methods Fail)
Newer Room versions (2.4+, default in 2022 Android Studio) use Write-Ahead Logging (WAL) by default. This means your data isn’t just stored in the main .db file—there are two companion files: your-db-name-wal and your-db-name-shm. Older guides often skip copying these, leading to corrupted databases or missing data after restore.
Core Steps to Follow
- Close the database before copying to avoid file locks or incomplete writes.
- Copy all three files (main db, WAL, SHM) to your backup location.
- For restore: Close the current database, replace the existing files with the backup copies, then re-open the database.
Kotlin Implementation
First, make sure your AppDatabase is a singleton (standard for Room):
@Database(entities = [YourEntity::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun yourDao(): YourDao companion object { const val DATABASE_NAME = "your_database" @Volatile private var INSTANCE: AppDatabase? = null fun getInstance(context: Context): AppDatabase { return INSTANCE ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, AppDatabase::class.java, DATABASE_NAME ).build() INSTANCE = instance instance } } } }
Backup Function
fun backupDatabase(context: Context, backupDirPath: String) { val db = AppDatabase.getInstance(context) // Close the database to ensure all pending writes are finalized db.close() val dbFile = context.getDatabasePath(AppDatabase.DATABASE_NAME) val walFile = File(dbFile.absolutePath + "-wal") val shmFile = File(dbFile.absolutePath + "-shm") // Create backup directory if it doesn't exist val backupDir = File(backupDirPath) if (!backupDir.exists()) backupDir.mkdirs() // Copy all required files try { copyFile(dbFile, File(backupDir, AppDatabase.DATABASE_NAME)) if (walFile.exists()) copyFile(walFile, File(backupDir, "${AppDatabase.DATABASE_NAME}-wal")) if (shmFile.exists()) copyFile(shmFile, File(backupDir, "${AppDatabase.DATABASE_NAME}-shm")) // Re-open the database for future use AppDatabase.getInstance(context) } catch (e: IOException) { // Handle error (e.g., show toast, log exception) e.printStackTrace() } } private fun copyFile(source: File, dest: File) { source.inputStream().use { input -> dest.outputStream().use { output -> input.copyTo(output) } } }
Restore Function
fun restoreDatabase(context: Context, backupDirPath: String) { val db = AppDatabase.getInstance(context) db.close() val dbFile = context.getDatabasePath(AppDatabase.DATABASE_NAME) val walFile = File(dbFile.absolutePath + "-wal") val shmFile = File(dbFile.absolutePath + "-shm") // Delete existing database files to avoid conflicts dbFile.delete() walFile.delete() shmFile.delete() try { // Copy backup files back to original location copyFile(File(backupDirPath, AppDatabase.DATABASE_NAME), dbFile) copyFile(File(backupDirPath, "${AppDatabase.DATABASE_NAME}-wal"), walFile) copyFile(File(backupDirPath, "${AppDatabase.DATABASE_NAME}-shm"), shmFile) // Re-open the database AppDatabase.getInstance(context) } catch (e: IOException) { // Handle error e.printStackTrace() } }
Java Implementation
Singleton Database Class
@Database(entities = {YourEntity.class}, version = 1) public abstract class AppDatabase extends RoomDatabase { public abstract YourDao yourDao(); public static final String DATABASE_NAME = "your_database"; private static volatile AppDatabase INSTANCE; public static AppDatabase getInstance(Context context) { if (INSTANCE == null) { synchronized (AppDatabase.class) { if (INSTANCE == null) { INSTANCE = Room.databaseBuilder( context.getApplicationContext(), AppDatabase.class, DATABASE_NAME ).build(); } } } return INSTANCE; } }
Backup Function
public static void backupDatabase(Context context, String backupDirPath) { AppDatabase db = AppDatabase.getInstance(context); db.close(); File dbFile = context.getDatabasePath(AppDatabase.DATABASE_NAME); File walFile = new File(dbFile.getAbsolutePath() + "-wal"); File shmFile = new File(dbFile.getAbsolutePath() + "-shm"); File backupDir = new File(backupDirPath); if (!backupDir.exists()) backupDir.mkdirs(); try { copyFile(dbFile, new File(backupDir, AppDatabase.DATABASE_NAME)); if (walFile.exists()) copyFile(walFile, new File(backupDir, AppDatabase.DATABASE_NAME + "-wal")); if (shmFile.exists()) copyFile(shmFile, new File(backupDir, AppDatabase.DATABASE_NAME + "-shm")); // Re-open database AppDatabase.getInstance(context); } catch (IOException e) { e.printStackTrace(); } } private static void copyFile(File source, File dest) throws IOException { try (InputStream input = new FileInputStream(source); OutputStream output = new FileOutputStream(dest)) { byte[] buffer = new byte[1024]; int length; while ((length = input.read(buffer)) > 0) { output.write(buffer, 0, length); } } }
Restore Function
public static void restoreDatabase(Context context, String backupDirPath) { AppDatabase db = AppDatabase.getInstance(context); db.close(); File dbFile = context.getDatabasePath(AppDatabase.DATABASE_NAME); File walFile = new File(dbFile.getAbsolutePath() + "-wal"); File shmFile = new File(dbFile.getAbsolutePath() + "-shm"); dbFile.delete(); walFile.delete(); shmFile.delete(); try { copyFile(new File(backupDirPath, AppDatabase.DATABASE_NAME), dbFile); copyFile(new File(backupDirPath, AppDatabase.DATABASE_NAME + "-wal"), walFile); copyFile(new File(backupDirPath, AppDatabase.DATABASE_NAME + "-shm"), shmFile); // Re-open database AppDatabase.getInstance(context); } catch (IOException e) { e.printStackTrace(); } }
Critical Notes to Avoid Issues
- WAL Files: Never skip copying the
-waland-shmfiles—this is the #1 mistake in outdated guides. - Permissions: If backing up to external storage, use Scoped Storage (Android 10+) or request the
MANAGE_EXTERNAL_STORAGEpermission (only for legacy use cases). Backing up to your app’s private directory (context.getFilesDir()) requires no permissions. - Error Handling: Always wrap file operations in try-catch blocks to handle IO exceptions (e.g., missing files, permission denied).
- Background Threads: These operations should run on a background thread (use
Coroutinesin Kotlin orAsyncTask/ExecutorServicein Java) to avoid blocking the main UI thread.
内容的提问来源于stack exchange,提问作者Esmail

