如何用Riverpod初始化Isar?现有初始化偶发失败求排查优化
Isar初始化偶尔失败的问题排查与优化方案
核心问题分析
1. 未向外传递初始化异常
你的initIsar方法捕获了异常但未抛出,导致FutureProvider会错误地认为初始化已成功,返回的IsarService实例中isar变量(late修饰)可能未被赋值,后续调用时就会触发LateInitializationError,也就是你看到的“Isar not initialized.”错误。
2. 单例与FutureProvider的潜在冲突
虽然IsarService是单例,但FutureProvider每次被重新订阅时,都会再次调用initIsar方法。如果第一次初始化失败,后续重复调用可能导致不可预期的状态;如果初始化成功后再次调用Isar.open,也可能引发数据库连接冲突。
3. 启动初始化的时机与错误处理缺失
用Future.delayed(Duration.zero)在initState中触发初始化,时机不可靠,且初始化失败后没有重试或降级逻辑,进而出现无报错但无法拉取数据的情况。
优化后的代码实现
1. 修复IsarService类
class IsarService { Isar? _isar; static final IsarService _instance = IsarService._internal(); IsarService._internal(); factory IsarService() => _instance; final schemaList = [ SearchedUserLocationSchema, SavedLocationModelSchema, SelectedUserLocationSchema ]; bool get isInitialized => _isar != null && _isar!.isOpen; Future<void> initIsar() async { if (isInitialized) return; // 避免重复初始化 try { _isar = await Isar.open( maxSizeMiB: Isar.defaultMaxSizeMiB, directory: (await getApplicationSupportDirectory()).path, schemas: schemaList.cast<CollectionSchema>(), inspector: true, ); kLog('Isar DB Initialized'); } catch (e, s) { kLog('Isar初始化失败: $e'); kLog(s); rethrow; // 抛出异常,让上层感知初始化失败 } } // 提供安全的Isar实例访问方式 Isar get isar { if (!isInitialized) { throw StateError('Isar not initialized.'); } return _isar!; } }
2. 优化Riverpod Provider
改用AsyncNotifierProvider更好地管理初始化状态,支持重试:
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'isar_service.dart'; class IsarNotifier extends AsyncNotifier<IsarService> { @override Future<IsarService> build() async { final service = IsarService(); await service.initIsar(); return service; } // 提供重试初始化的方法 Future<void> retryInit() async { state = const AsyncLoading(); try { final service = IsarService(); await service.initIsar(); state = AsyncData(service); } catch (e, s) { kLog('重试初始化失败: $e'); kLog(s); state = AsyncError(e, s); } } } final isarServiceProvider = AsyncNotifierProvider<IsarNotifier, IsarService>( () => IsarNotifier(), );
3. 优化应用启动初始化逻辑
@override void initState() { super.initState(); _initIsar(); } Future<void> _initIsar() async { final asyncValue = ref.watch(isarServiceProvider); asyncValue.when( data: (_) => kLog('Isar初始化完成'), loading: () => kLog('Isar初始化中'), error: (e, s) { kLog('Isar初始化失败,准备重试'); // 延迟1秒重试,可根据需求调整次数 Future.delayed(const Duration(seconds: 1), () { ref.read(isarServiceProvider.notifier).retryInit(); }); }, ); }
额外建议
- 权限检查: Android需确认
AndroidManifest.xml中是否添加存储权限(针对旧版本系统);iOS需确保Info.plist配置了沙箱权限。 - 状态前置判断: 业务代码中访问
isar前,先通过isInitialized判断状态,避免崩溃。 - 日志增强: 数据拉取失败时,输出
IsarService().isInitialized的状态,快速定位问题。
内容的提问来源于stack exchange,提问作者Abdul Kader Hridoy
相关产品推荐
相关产品推荐

