yandex / mapkit-android-demo

MapKit Android demo
Other
122 stars 61 forks source link

Fatal Exception при удалении объектов #73

Closed cdump closed 5 years ago

cdump commented 5 years ago

mapkit 3.3.1 у меня на карте около 1 тысячи объектов, и раз в минуту происходит обновление данных с сервера: какие-то объекты появляются, какие-то удаляются. На каждом обновлении очищать и добавлять все объекты очень долго, поэтому я решил накладывать diff'ы: удалять из слоя удаленные объекты и добавлять новые:

val objectsStorage = HashMap<Int, PlacemarkMapObject>();
mapObjects = ymap.addMapObjectLayer("myobjects")
// добавление obj:
val mapObj = mapObjects.addPlacemark(obj.coordinates, obj.icon)
objectsStorage[obj.id] = mapObj
// удаление obj_id
val mapObj = objectsStorage.remove(obj_id)!!
mapObjects.remove(mapObj)

Проблема в том, что иногда (похоже когда добавлений/удаление относительно много) случаются крэши:

Fatal Exception: java.lang.RuntimeException: Native object's weak_ptr for N6yandex4maps6mapkit3map9MapObjectE has expired
Exception stack trace (top 8 entries):
  # 0: 1525664  libcom.yandex.runtime.so _ZN6yandex4maps7runtime9backtrace9BacktraceC1Ev
  # 1: 1539072  libcom.yandex.runtime.so 
  # 2: 1538492  libcom.yandex.runtime.so _ZN6yandex4maps7runtime9ExceptionC2ERKNSt6__ndk112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS2_8SeverityE
  # 3: 1613580  libcom.yandex.mapkit.so 
  # 4: 4796688  libcom.yandex.mapkit.so 
  # 5: 4887868  libcom.yandex.mapkit.so 
  # 6: 4879336  libcom.yandex.mapkit.so 
  # 7: 4878484  libcom.yandex.mapkit.so Java_com_yandex_mapkit_map_internal_MapObjectCollectionBinding_remove__Lcom_yandex_mapkit_map_MapObject_2

       at com.yandex.mapkit.map.internal.MapObjectCollectionBinding.remove(MapObjectCollectionBinding.java)
...

Вопросы:

  1. добавление объектов можно же делать только из ui треда? или есть способ делать это, не блокируя его?
  2. как правильно накладывать diff'ы в моей схеме чтобы не ловить крэш?
eberkovich commented 5 years ago
  1. Все вызова mapkit должны производиться из ui потока.
  2. Такой креш означает, что происходит обращение к уже удаленному объекту. Дело в том PlacemarkMapObject полученный при вызове MapObjectCollection.addPlacemark по сути является слабой ссылкой, владеет всеми MapObject карта. Поэтому если позвать MapObjectCollection.remove()/clear(), а потом обратиться к сохраненному MapObject, будет брошено такое исключение.
cdump commented 5 years ago

@eberkovich у меня есть еще только одно место, где я взаимодействую с mapObjects: вызываю mapObjects.clear() из onDestroy() MainActivity

Добавил я это когда пытался обойти проблему https://github.com/yandex/mapkit-android-demo/issues/40#issuecomment-433701975 - сейчас я на 100% не помню что я этим решил, но 90% что следующую проблему: когда приложение находилось в фоновом режиме и Android решал его выгрузить из памяти (и соотв. позвал onDestroy), то когда пользователь решал возобновить работу приложения и проходила полная инициализация, то возникала та ошибка с global reference table overflow (max=51200). У меня такое число объектов, что 51200 хватает, но если увеличить число объектов в 2 раза - уже не хватит, и я предположил что при выгрузке приложения эти референсы почему-то не освобождались. Никакого теоретического подтверждения у меня этому нет, и звучит довольно странно - но это стабильно вопспроизводилось и вызов .clear() при onDestroy() эту проблему решал.

eberkovich commented 5 years ago

Не могли бы Вы приложить минимальный пример демонстрирующий проблему?

cdump commented 5 years ago

Ок, попробую вопроизвести и выложить пример, но скорее на след. выходных только полуится.

eberkovich commented 5 years ago

Кстати, по поводу блокирования ui потока: #40 (comment)

cdump commented 5 years ago

Про ui поток: addPlacemarks я пробовал, в моем случае оказалось на уровне обычного добавления или даже медленее (измерял не только добавление, а общее время: предварительную группировку по разным иконкам (~10 шт.), само добавление, сохранение в мой hashmap objectsStorage и т.п.), возможно я криво переписал ту часть когда экспериментировал с этим и у остальных оно действительно быстрее.

Да и без addPlacemarks достаточно быстро, особенно по сравнению с googlemaps :) Если же хочется быстрее - разбивка добавления на части по N штук и отложенный запуск добавления след. части по таймеру через 1-2 мс. хорошо повышало отзывчивость ui, хоть суммарное время и было дольше и было заметно как объекты появляются группами.

cdump commented 5 years ago

Добавил в onDestroy очитку своей мапы и крэши пропали. Думаю можно считать моим багом и закрывать issue.

AlexSuvorov2k commented 4 years ago

@cdump если не секрет, что именно указали в onDestroy?

gc986 commented 3 years ago

@cdump если не секрет, что именно указали в onDestroy?

Возможно это было что-то типа такого mapView.map.mapObjects.clear()

cdump commented 3 years ago

Указывал

private val objectsNormal: MapObjectCollection
private val objectsFocused: MapObjectCollection
private val currentPointToMyObject = HashMap<Pair<Double,Double>, MyMapObject>()
...
fun onDestroy() {
  objectsNormal.clear()
  objectsFocused.clear()
  currentPointToMyObject.clear()
}

где

objectsNormal = ymap.addMapObjectLayer("normal")
objectsFocused = ymap.addMapObjectLayer("focused")