fix(mining-app): 更新弹窗点击"立即更新"无反应 — 进度流订阅竞态修复

问题根因:
SelfHostedUpdater._listenToProgress() 在 initState 和 _startDownload 中
订阅 UpdateService.downloadProgressStream,但该 getter 返回
_downloadManager?.progressStream —— _downloadManager 在 downloadUpdate()
调用前为 null,?.listen 无操作。downloadUpdate() 创建新 _downloadManager
后,其 progressStream 无人监听,进度事件全部丢失,UI 无任何反馈。

修复:
1. UpdateService 新增持久化广播流 _downloadProgressController,
   downloadUpdate() 创建新 DownloadManager 后将其进度转发到此流
2. downloadProgressStream getter 改为返回持久化流(非 nullable)
3. SelfHostedUpdater 在 initState 中一次性订阅持久化流,
   dispose 时取消订阅,_startDownload 不再重复订阅

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-30 12:24:47 -08:00
parent bde7f0c53b
commit cfa0e2ca40
2 changed files with 25 additions and 10 deletions

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import '../models/version_info.dart';
@ -48,15 +49,13 @@ class _SelfHostedUpdaterState extends State<SelfHostedUpdater> {
DownloadProgress _progress = DownloadProgress.initial();
File? _downloadedFile;
bool _isDownloading = false;
StreamSubscription<DownloadProgress>? _progressSubscription;
@override
void initState() {
super.initState();
_listenToProgress();
}
void _listenToProgress() {
UpdateService.instance.downloadProgressStream?.listen((progress) {
// 广
_progressSubscription = UpdateService.instance.downloadProgressStream.listen((progress) {
if (mounted) {
setState(() {
_progress = progress;
@ -65,6 +64,12 @@ class _SelfHostedUpdaterState extends State<SelfHostedUpdater> {
});
}
@override
void dispose() {
_progressSubscription?.cancel();
super.dispose();
}
Future<void> _startDownload() async {
setState(() {
_isDownloading = true;
@ -73,8 +78,6 @@ class _SelfHostedUpdaterState extends State<SelfHostedUpdater> {
);
});
_listenToProgress();
final file = await UpdateService.instance.downloadUpdate(widget.versionInfo);
if (mounted) {

View File

@ -51,6 +51,10 @@ class UpdateService {
final _updateAvailableController = StreamController<VersionInfo?>.broadcast();
Stream<VersionInfo?> get updateAvailableStream => _updateAvailableController.stream;
/// DownloadManager
final _downloadProgressController = StreamController<DownloadProgress>.broadcast();
StreamSubscription? _downloadProgressSubscription;
VersionInfo? _latestVersion;
VersionInfo? get latestVersion => _latestVersion;
@ -126,14 +130,20 @@ class UpdateService {
///
Future<File?> downloadUpdate(VersionInfo versionInfo) async {
//
await _downloadProgressSubscription?.cancel();
_downloadManager?.dispose();
_downloadManager = DownloadManager(versionInfo: versionInfo);
// DownloadManager 广
_downloadProgressSubscription = _downloadManager!.progressStream.listen(
(progress) => _downloadProgressController.add(progress),
);
return await _downloadManager!.startDownload();
}
///
Stream<DownloadProgress>? get downloadProgressStream =>
_downloadManager?.progressStream;
/// 广
Stream<DownloadProgress> get downloadProgressStream =>
_downloadProgressController.stream;
///
void cancelDownload() {
@ -166,7 +176,9 @@ class UpdateService {
}
void dispose() {
_downloadProgressSubscription?.cancel();
_downloadManager?.dispose();
_downloadProgressController.close();
_updateAvailableController.close();
}
}