如何正确重启Android应用 无需System.exit(0)获取新Room数据库实例
解决方案
你遇到的核心问题是数据库同时存在静态单例缓存和Dagger Application作用域缓存,只要应用进程没有销毁,两个缓存都会保留旧的数据库实例,不需要强制杀进程也可以实现需求,优先推荐无感知重启方案:
方案一:手动重置数据库实例(无需杀进程,体验最优)
1. 修改MyDatabase增加重置方法
需要先关闭原有数据库连接,避免文件句柄泄漏和文件锁问题:
@Database(entities = {Person.class, Notification.class}, version = 1, exportSchema = false) @TypeConverters({DateConverter.class}) public abstract class MyDatabase extends RoomDatabase { private static final Object LOCK = new Object(); private static final String DATABASE_NAME = Constants.DATABASE_NAME; private static MyDatabase sInstance; public static MyDatabase getInstance(final Context context) { if (sInstance == null) { synchronized (LOCK) { Log.d("Logger", "Get new DB instance"); sInstance = Room.databaseBuilder( context.getApplicationContext(), MyDatabase.class, MyDatabase.DATABASE_NAME) .setJournalMode(RoomDatabase.JournalMode.TRUNCATE) .fallbackToDestructiveMigration() .build(); } } return sInstance; } // 新增重置方法 public static void resetInstance() { synchronized (LOCK) { if (sInstance != null) { sInstance.close(); // 必须关闭原有连接,否则覆盖文件会报错 sInstance = null; } } } }
2. 调整Dagger配置
你原本的Dagger Module里应该是用@Singleton注解提供数据库实例,相当于Dagger也做了一层实例缓存,就算静态单例重置了,Dagger还是会返回旧的缓存实例,所以修改提供逻辑,去掉@Singleton注解,直接复用MyDatabase自带的静态单例即可:
// 原有写法(会导致Dagger额外缓存实例) // @Provides // @Singleton // MyDatabase provideMyDatabase(Context context) { // return MyDatabase.getInstance(context); // } // 调整后写法 @Provides MyDatabase provideMyDatabase(Context context) { return MyDatabase.getInstance(context); }
3. 恢复备份流程
private void restoreDatabase() { // 1. 先停止所有正在执行的数据库读写操作,关闭所有使用数据库的页面 // 2. 用备份文件覆盖本地数据库文件,同时删除同目录下的.db-wal、.db-shm日志文件 // 3. 重置数据库单例 MyDatabase.resetInstance(); // 4. 重启首页,无感知刷新 Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); }
方案二:规范的应用进程重启方案(无需改现有Dagger配置)
如果不想调整现有依赖注入逻辑,必须重启进程的话,用系统AlarmManager配合PendingIntent实现,比直接调用System.exit(0)更稳定,不会出现闪退感知:
private void restartApp() { Context appContext = getApplicationContext(); // 获取应用启动页Intent Intent launchIntent = appContext.getPackageManager().getLaunchIntentForPackage(appContext.getPackageName()); PendingIntent pendingIntent = PendingIntent.getActivity( appContext, 1001, launchIntent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE ); // 100ms后重启应用 AlarmManager alarmManager = (AlarmManager) appContext.getSystemService(Context.ALARM_SERVICE); alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent); // 销毁当前进程 System.exit(0); }
注意事项
- 恢复备份前必须确认没有后台线程/服务正在执行数据库操作,否则会出现文件锁冲突、数据损坏问题
- 覆盖数据库文件时必须同步删除同目录下的
.db-wal、.db-shm日志文件,否则Room会优先读取日志内容导致备份不生效 - 如果应用存在多进程使用数据库的场景,需要确保所有关联进程都已停止再执行备份恢复操作
内容的提问来源于stack exchange,提问作者Chris P.
相关产品推荐
相关产品推荐

