Files
AndroidAisMap/MAP_HANG_REAL_FIX.md
T
Grigo b5aee265bc 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
2025-10-02 09:15:33 +03:00

4.3 KiB
Raw Blame History

РЕАЛЬНОЕ исправление зависаний карты и кнопок

НАЙДЕНА ИСТИННАЯ ПРИЧИНА!

Главная проблема: В MapLibreMapImpl.updateOwnVesselPosition() вызывалось ТРИ отдельных uiHandler.post() операции:

// СТАРЫЙ КОД - ПРОБЛЕМНЫЙ:
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() проблематичен:

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:

// Новые переменные для 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:

@Override
public void updateOwnVesselPosition(Vessel vessel) {
    // Данные обновляются СРАЗУ (не блокирующее)
    JSONObject feature = buildFeature(...);
    idToFeature.put("own_vessel", feature);
    
    // Throttled обновление карты
    updateMapThrottled(vessel);
}

3. Батчевое обновление карты:

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 работает правильно.