feat(app): 激活模型变化时重建推理 worker(无模型仅预览并提示选择)
ModelManager 激活集变化(下载完成自动激活/取消激活/目录刷新)时经 _onModelsChanged 触发 _reloadWorker:激活集未变直接跳过,变了则重建 worker+analyzer 并重启帧流;无激活模型时仅预览,横幅区分 「未使用模型:点右上角设置…」与「识别模型加载失败…」。
This commit is contained in:
@@ -38,6 +38,10 @@ class _CameraScreenState extends State<CameraScreen> {
|
|||||||
Map<dynamic, dynamic> _nativeStats = const {};
|
Map<dynamic, dynamic> _nativeStats = const {};
|
||||||
Timer? _statsTimer;
|
Timer? _statsTimer;
|
||||||
|
|
||||||
|
/// 当前 worker 已加载的模型 id 集合(激活集变化对比用)
|
||||||
|
Set<int> _loadedModelIds = const {};
|
||||||
|
bool _reloadingWorker = false;
|
||||||
|
|
||||||
void _openSettings() {
|
void _openSettings() {
|
||||||
final vm = _viewModel;
|
final vm = _viewModel;
|
||||||
if (vm == null) return;
|
if (vm == null) return;
|
||||||
@@ -116,6 +120,8 @@ class _CameraScreenState extends State<CameraScreen> {
|
|||||||
WakelockPlus.enable();
|
WakelockPlus.enable();
|
||||||
// 每秒轮询原生侧帧状态:无帧时诊断行也能实时刷新(camErr/计数)
|
// 每秒轮询原生侧帧状态:无帧时诊断行也能实时刷新(camErr/计数)
|
||||||
_statsTimer = Timer.periodic(const Duration(seconds: 1), (_) => _pollStats());
|
_statsTimer = Timer.periodic(const Duration(seconds: 1), (_) => _pollStats());
|
||||||
|
// 模型清单激活集变化(下载完成自动激活/取消激活)时重建推理 worker
|
||||||
|
ModelManager.instance.addListener(_onModelsChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pollStats() async {
|
Future<void> _pollStats() async {
|
||||||
@@ -132,8 +138,7 @@ class _CameraScreenState extends State<CameraScreen> {
|
|||||||
setState(() => _permissionGranted = granted);
|
setState(() => _permissionGranted = granted);
|
||||||
if (!granted) return;
|
if (!granted) return;
|
||||||
|
|
||||||
// 模型热更新:优先使用已下载的数据集模型(启动时后台拉取;此处兜底等待,
|
// 模型热更新:启动拉取目录(只拉不下载),此处兜底等待;未下载模型时仅预览
|
||||||
// 下载慢/失败不阻塞相机启动——无下载模型时仅预览)
|
|
||||||
if (!ModelManager.instance.ready) {
|
if (!ModelManager.instance.ready) {
|
||||||
try {
|
try {
|
||||||
await ModelManager.instance
|
await ModelManager.instance
|
||||||
@@ -141,23 +146,55 @@ class _CameraScreenState extends State<CameraScreen> {
|
|||||||
.timeout(const Duration(seconds: 15));
|
.timeout(const Duration(seconds: 15));
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
// 模型加载/推理在后台 isolate,不阻塞 UI;worker 为 null 时仅预览并提示
|
await _reloadWorker();
|
||||||
final worker = await DetectorWorker.create(
|
await _startCamera();
|
||||||
models: ModelManager.instance.models);
|
}
|
||||||
final viewModel = CameraViewModel(reminder: Reminder());
|
|
||||||
viewModel.setModelReady(worker != null);
|
/// 用当前激活模型重建推理 worker(激活集变化/启动时调用);
|
||||||
final analyzer = FrameAnalyzer(worker: worker, viewModel: viewModel);
|
/// worker 为 null(无激活模型或加载失败)时仅预览并提示。
|
||||||
if (!mounted) {
|
Future<void> _reloadWorker() async {
|
||||||
analyzer.dispose();
|
if (_reloadingWorker) return;
|
||||||
viewModel.dispose();
|
_reloadingWorker = true;
|
||||||
|
try {
|
||||||
|
final models = ModelManager.instance.models;
|
||||||
|
final ids = models.map((m) => m.datasetId).toSet();
|
||||||
|
if (_viewModel != null &&
|
||||||
|
_loadedModelIds.length == ids.length &&
|
||||||
|
_loadedModelIds.containsAll(ids)) {
|
||||||
|
return; // 激活集未变(进度/目录刷新通知)直接跳过
|
||||||
|
}
|
||||||
|
final worker = await DetectorWorker.create(models: models);
|
||||||
|
_loadedModelIds = ids;
|
||||||
|
final vm = _viewModel ?? CameraViewModel(reminder: Reminder());
|
||||||
|
vm.setModelReady(worker != null);
|
||||||
|
final analyzer = FrameAnalyzer(worker: worker, viewModel: vm);
|
||||||
|
final old = _analyzer;
|
||||||
|
if (!mounted) {
|
||||||
|
analyzer.dispose();
|
||||||
|
if (vm != _viewModel) vm.dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_viewModel = vm;
|
||||||
|
_analyzer = analyzer;
|
||||||
|
});
|
||||||
|
old?.worker?.dispose();
|
||||||
|
// 相机已启动:重启帧流绑定新 analyzer(start 内部先 stop 再订阅)
|
||||||
|
if (mounted && _cameraController != null) {
|
||||||
|
await _cameraController!.start(analyzer);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_reloadingWorker = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onModelsChanged() {
|
||||||
|
final ids = ModelManager.instance.models.map((m) => m.datasetId).toSet();
|
||||||
|
if (_loadedModelIds.length == ids.length &&
|
||||||
|
_loadedModelIds.containsAll(ids)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setState(() {
|
_reloadWorker();
|
||||||
_viewModel = viewModel;
|
|
||||||
_analyzer = analyzer;
|
|
||||||
});
|
|
||||||
|
|
||||||
await _startCamera();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _startCamera() async {
|
Future<void> _startCamera() async {
|
||||||
@@ -193,6 +230,7 @@ class _CameraScreenState extends State<CameraScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
ModelManager.instance.removeListener(_onModelsChanged);
|
||||||
_statsTimer?.cancel();
|
_statsTimer?.cancel();
|
||||||
WakelockPlus.disable();
|
WakelockPlus.disable();
|
||||||
_cameraController?.stop();
|
_cameraController?.stop();
|
||||||
@@ -333,7 +371,9 @@ class _CameraScreenState extends State<CameraScreen> {
|
|||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'识别模型加载失败:${DetectorWorker.lastLoadError ?? '未知原因'}\n最后步骤:${DetectorWorker.lastLog ?? '-'}',
|
ModelManager.instance.models.isEmpty
|
||||||
|
? '未使用模型:点右上角设置,在「模型清单」中选择要使用的模型'
|
||||||
|
: '识别模型加载失败:${DetectorWorker.lastLoadError ?? '未知原因'}\n最后步骤:${DetectorWorker.lastLog ?? '-'}',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(color: Colors.orange, fontSize: 14),
|
style: const TextStyle(color: Colors.orange, fontSize: 14),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user