mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-06-11 09:17:50 +02:00
map updates slowly with retry logic as tiles become available
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
import 'dart:math';
|
||||
import 'dart:io';
|
||||
import 'dart:async';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// Fetches a tile from OSM, with in-memory retries/backoff.
|
||||
/// Global semaphore to limit simultaneous tile fetches
|
||||
final _tileFetchSemaphore = _SimpleSemaphore(4); // Max 4 concurrent
|
||||
|
||||
/// Fetches a tile from OSM, with in-memory retries/backoff, and global concurrency limit.
|
||||
/// Returns tile image bytes, or throws on persistent failure.
|
||||
Future<List<int>> fetchOSMTile({
|
||||
required int z,
|
||||
@@ -19,6 +24,7 @@ Future<List<int>> fetchOSMTile({
|
||||
10000 + random.nextInt(4000) - 2000
|
||||
];
|
||||
while (true) {
|
||||
await _tileFetchSemaphore.acquire();
|
||||
try {
|
||||
print('[fetchOSMTile] FETCH $z/$x/$y');
|
||||
attempt++;
|
||||
@@ -40,6 +46,36 @@ Future<List<int>> fetchOSMTile({
|
||||
final delay = delays[attempt - 1].clamp(0, 60000);
|
||||
print("[fetchOSMTile] Attempt $attempt for $z/$x/$y failed: $e. Retrying in ${delay}ms.");
|
||||
await Future.delayed(Duration(milliseconds: delay));
|
||||
} finally {
|
||||
_tileFetchSemaphore.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple counting semaphore, suitable for single-thread Flutter concurrency
|
||||
class _SimpleSemaphore {
|
||||
final int _max;
|
||||
int _current = 0;
|
||||
final List<VoidCallback> _queue = [];
|
||||
_SimpleSemaphore(this._max);
|
||||
|
||||
Future<void> acquire() async {
|
||||
if (_current < _max) {
|
||||
_current++;
|
||||
return;
|
||||
} else {
|
||||
final c = Completer<void>();
|
||||
_queue.add(() => c.complete());
|
||||
await c.future;
|
||||
}
|
||||
}
|
||||
|
||||
void release() {
|
||||
if (_queue.isNotEmpty) {
|
||||
final callback = _queue.removeAt(0);
|
||||
callback();
|
||||
} else {
|
||||
_current--;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,7 @@ class _MapViewState extends State<MapView> {
|
||||
late final MapController _controller;
|
||||
final MapDataProvider _mapDataProvider = MapDataProvider();
|
||||
final Debouncer _debounce = Debouncer(const Duration(milliseconds: 500));
|
||||
Debouncer? _debounceTileLayerUpdate;
|
||||
|
||||
StreamSubscription<Position>? _positionSub;
|
||||
LatLng? _currentLatLng;
|
||||
@@ -111,6 +112,7 @@ class _MapViewState extends State<MapView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_debounceTileLayerUpdate = Debouncer(const Duration(milliseconds: 50),);
|
||||
// Kick off offline area loading as soon as map loads
|
||||
OfflineAreaService();
|
||||
_controller = widget.controller;
|
||||
@@ -239,7 +241,9 @@ class _MapViewState extends State<MapView> {
|
||||
children: [
|
||||
TileLayer(
|
||||
tileProvider: TileProviderWithCache(
|
||||
onTileCacheUpdated: () { if (mounted) setState(() {}); },
|
||||
onTileCacheUpdated: () {
|
||||
if (_debounceTileLayerUpdate != null) _debounceTileLayerUpdate!(() { if (mounted) setState(() {}); });
|
||||
},
|
||||
),
|
||||
urlTemplate: 'unused-{z}-{x}-{y}',
|
||||
tileSize: 256,
|
||||
|
||||
Reference in New Issue
Block a user