Flutter切换页面报SqfliteDatabaseException(database_closed)错误求助
问题根因
- 核心逻辑错误:
initDb方法中每次执行都会调用await deleteDatabase(path)删除现有数据库,只要触发数据库重新初始化逻辑,就会先销毁现有数据库文件,此时如果前一个页面的数据库操作还在执行,会直接抛出数据库关闭异常。同时该逻辑会导致所有运行时产生的用户数据每次初始化都被清空,本身也不符合业务逻辑要求。 - 潜在并发问题:未做初始化并发控制,如果两个页面同时触发数据库初始化请求,会出现竞态条件,可能导致第一个初始化的数据库实例被覆盖,旧实例被异常关闭。
- 可能的触发点:如果项目其他位置手动调用了
Database.close()方法,会把静态的_db实例关闭,但_db变量并不会被置为null,后续调用db getter会直接返回已经关闭的实例,触发报错。
修复方案
- 调整初始化逻辑,仅在数据库不存在时复制assets预制库,删除每次初始化删库的逻辑:
Future<Database> initDb() async { var dbFolder = await getDatabasesPath(); String path = join(dbFolder, 'app.db'); // 仅当数据库文件不存在时才复制预制库 bool dbExists = await File(path).exists(); if (!dbExists) { try { await Directory(dirname(path)).create(recursive: true); } catch (_) {} ByteData data = await rootBundle.load(join("assets/database", "bankingapp.db")); List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); await File(path).writeAsBytes(bytes, flush: true); } return await openDatabase(path); }
- 新增初始化锁,避免并发初始化的竞态问题,修改DbHelper的实例控制逻辑:
class DbHelper { static Database? _db; // 初始化锁,防止并发重复初始化 static Completer<Database>? _initCompleter; Future<Database> get db async { if (_db != null) return _db!; if (_initCompleter != null) { return _initCompleter!.future; } _initCompleter = Completer<Database>(); try { _db = await initDb(); _initCompleter!.complete(_db); } catch (e) { _initCompleter!.completeError(e); _initCompleter = null; rethrow; } return _db!; } // 其余getHistories、getCoins方法保持不变 }
- 排查全项目代码,删除非必要的
Database.close()调用。如果确实有关闭数据库的业务需求,新增配套的关闭方法,确保关闭后清空缓存的实例:
Future<void> closeDb() async { if (_db != null) { await _db!.close(); _db = null; _initCompleter = null; } }
内容的提问来源于stack exchange,提问作者MaybeExpertDBA
相关产品推荐
相关产品推荐

