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

Android Studio 2022版本下Room数据库正确备份与恢复方法(Kotlin/Java实现)

Correct Room Database Backup & Restore for 2022 Android Studio (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

  1. Close the database before copying to avoid file locks or incomplete writes.
  2. Copy all three files (main db, WAL, SHM) to your backup location.
  3. 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 -wal and -shm files—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_STORAGE permission (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 Coroutines in Kotlin or AsyncTask/ExecutorService in Java) to avoid blocking the main UI thread.

内容的提问来源于stack exchange,提问作者Esmail

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 20:02:50