Flutter+Firestore加载千张以上图片时报数据库锁警告并崩溃如何解决
database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
问题原因
该报错本质是Firestore本地持久化依赖的SQLite数据库被长时间抢占锁触发超时,结合你的业务场景具体原因如下:
- 一次性通过StreamBuilder拉取1000+条Firestore文档数据,大量快照写入本地持久化缓存时触发了高频数据库写入操作
cached_network_image同时发起上千张图片的并发下载和缓存写入请求,和Firestore的写入操作抢占本地数据库资源,导致锁冲突超时- 大规模并发IO操作耗尽设备系统资源,最终导致Flutter引擎和原生层连接断开,触发
lost connection to device崩溃 - 少量数据加载时IO压力小,不会触发锁冲突,因此运行正常
解决方案
1. 改造Firestore数据加载逻辑
不要一次性拉取全量1000+条数据,采用分页加载方案,每次仅加载当前视口及预加载区域需要的数据,示例代码如下:
// 首次查询,每页加载20条 Query wallpaperQuery = FirebaseFirestore.instance .collection('wallpapers') .orderBy('upload_time') .limit(20); // 滚动到列表底部加载下一页时,用最后一条文档作为游标查询 Query nextPageQuery = wallpaperQuery.startAfterDocument(lastFetchedDoc).limit(20);
如果你的应用不需要离线访问Firestore数据,可以直接关闭本地持久化,从根源避免本地数据库写入冲突:
FirebaseFirestore.instance.settings = const Settings( persistenceEnabled: false, );
2. 优化图片加载配置
调整cached_network_image的缓存和并发策略,降低IO压力:
- 限制图片缓存尺寸,仅缓存和展示尺寸匹配的压缩图,避免写入超大原图到本地
- 设置唯一缓存key,避免同一张图片重复下载缓存
- 列表组件开启视图回收,取消不可见区域的图片加载请求
示例配置:
CachedNetworkImage( imageUrl: wallpaperUrl, cacheKey: wallpaperId, // 用壁纸唯一ID作为缓存key maxHeightDiskCache: 800, // 按你实际展示的最大高度设置 maxWidthDiskCache: 450, fit: BoxFit.cover, )
使用GridView/ListView承载壁纸时添加如下配置,回收不可见项的资源:
ListView.builder( addAutomaticKeepAlives: false, addRepaintBoundaries: true, itemCount: wallpaperList.length, itemBuilder: (context, index) => // 你的壁纸项组件 )
3. 错峰IO操作
避免Firestore数据拉取和图片批量加载同时执行,可先拉取Firestore文档基础数据,再随列表滚动逐批触发图片加载,降低同一时间的IO并发量。
内容的提问来源于stack exchange,提问作者Aditya Hathlee
相关产品推荐
相关产品推荐

