feat: новая архитектура UI и расширенная визуализация AIS

Архитектурные улучшения:
- Внедрен UIRenderingCoordinator с централизованным throttling
- Решены проблемы зависания UI через батчинг операций карты
- Добавлен VesselPathController для отслеживания маршрутов
- Реализован MapLibreMapImpl как альтернатива Яндекс.Картам

Визуализация AIS:
- Добавлены векторные иконки для всех типов судов
- Разделение Class A/B судов с соответствующими иконками
- Иконки навигационных статусов (anchor, moored, engine, sail)
- Улучшенный CursorOverlay с информацией о судах

Производительность:
- Throttling UI обновлений (vessel: 500ms, AIS: 1s, paths: 2s)
- Устранение утечек Handler объектов
- Оптимизация GeoJSON операций в MapLibre
This commit is contained in:
2025-10-02 09:15:33 +03:00
parent 41432665ea
commit b5aee265bc
85 changed files with 7132 additions and 449 deletions
+94
View File
@@ -0,0 +1,94 @@
# РЕАЛЬНОЕ исправление зависаний карты и кнопок
## НАЙДЕНА ИСТИННАЯ ПРИЧИНА!
**Главная проблема**: В `MapLibreMapImpl.updateOwnVesselPosition()` вызывалось **ТРИ отдельных** `uiHandler.post()` операции:
```java
// СТАРЫЙ КОД - ПРОБЛЕМНЫЙ:
uiHandler.post(() -> updateOwnVesselPathSource("own_vessel", pathCoords)); // 1
uiHandler.post(() -> updateOwnVesselPredictionSource("own_vessel", vessel)); // 2
uiHandler.post(() -> refreshGeoJson()); // 3
```
## Проблемы создававшие блокировки:
### 1. **Множественные UI операции**
Каждое обновление GPS/NMEA вызывало **4 раза** `updateOwnVesselPosition` из AppController:
- Из GPS `onLocationUpdated` (2 раза)
- Из NMEA `onVesselUpdated` (2 раза)
### 2. **Тяжелые операции в UI потоке**:
- `refreshGeoJson()` - пересоздание всей GeoJSON каждый раз
- `updateOwnVesselPathSource()` - обновление источника с множеством координат
- `updateOwnVesselPredictionSource()` - расчет прогноза
### 3. **reffreshGeoJson() проблематичен**:
```java
private void refreshGeoJson() {
JSONObject fc = new JSONObject();
fc.put("type", "FeatureCollection");
JSONArray features = new JSONArray();
for (JSONObject f : idToFeature.values()) { // Итерация по ВСЕМ судам
features.put(f); // Объект преобразуется в JSON
}
fc.put("features", features);
source.setGeoJson(fc.toString()); // Создание большой строки!
}
```
## ВНЕСЕННЫЕ ИСПРАВЛЕНИЯ:
### 1. **Throttling система в MapLibreMapImpl**:
```java
// Новые переменные для throttling
private final android.os.Handler mapUpdateHandler = new android.os.Handler(android.os.Looper.getMainLooper());
private Runnable mapUpdateRunnable;
private boolean mapUpdatePending = false;
private static final long MAP_UPDATE_DELAY = 500; // 500ms throttling
```
### 2. **Переработанный updateOwnVesselPosition**:
```java
@Override
public void updateOwnVesselPosition(Vessel vessel) {
// Данные обновляются СРАЗУ (не блокирующее)
JSONObject feature = buildFeature(...);
idToFeature.put("own_vessel", feature);
// Throttled обновление карты
updateMapThrottled(vessel);
}
```
### 3. **Батчевое обновление карты**:
```java
private void updateMapBatched(Vessel vessel) {
uiHandler.post(() -> {
// ВСЕ операции в ОДНОМ UI вызове:
updateOwnVesselPathSource("own_vessel", pathCoords);
updateOwnVesselPredictionSource("own_vessel", vessel);
refreshGeoJson(); // Только один раз!
});
}
```
### 4. **Убрали дублированные вызовы в AppController**:
- Удалили **2 избыточных** вызова `updateOwnVesselPosition`
- Теперь остается только **1 throttled** вызов вместо **4 обычных**
## Результат:
-**Throttling**: вместо постоянных обновлений - 1 раз в 500мс
-**Батчинг**: вместо 3 отдельных UI вызовов - 1 объединенный
-**Дедупликация**: вместо 4 вызовов из AppController - 1 throttled
-**Защита от зависания**: cleanup handler'ов в cleanup()
## Ожидаемый эффект:
1. **Карта и кнопки перестанут зависать**
2. **Доквиджеты продолжат работать** (они не затрагивались)
3. **Фоновые процессы не пострадают**
4. **Обновления карты станут плавными** вместо лагающих
**Ключевая диагностика**: Смотрите в логах `"Карта обновлена батчом"` - это означает что throttling работает правильно.