volga-volga / react-native-yamap

React Native Yandex Maps | Яндекс Карты | Yandex.MapKit implementation for react native | YandexMaps
151 stars 84 forks source link

Android. Краш PlacemarkMapObjectBinding.java #102

Closed DazzlingFame closed 2 years ago

DazzlingFame commented 2 years ago

Краш в

PlacemarkMapObjectBinding.java
com.yandex.mapkit.map.internal.PlacemarkMapObjectBinding.setGeometry

Рендерю компоненты Marker, внутри которых лежат компоненты с фиксированной шириной и без указания высоты (при указании фиксированной высоты тоже падает).

Маркеры рендерю по данным из редакса, которые обновляются при изменении положения карты.
Падает не всегда при 1 рендере меркеров, иногда удается 3-4 раза поменять позицию на карте до того, как упадёт

Окружение

  "react-native": "0.65.1",
  "react-native-yamap": "4.0.3",
  Android 11

StackTrace:

Fatal Exception: com.facebook.react.bridge.JSApplicationIllegalArgumentException: Error while updating property 'point' of a view managed by: YamapMarker

   at com.facebook.react.uimanager.ViewManagersPropertyCache$PropSetter.updateViewProp(ViewManagersPropertyCache.java:102)
   at com.facebook.react.uimanager.ViewManagerPropertyUpdater$FallbackViewManagerSetter.setProperty(ViewManagerPropertyUpdater.java:136)
   at com.facebook.react.uimanager.ViewManagerPropertyUpdater.updateProps(ViewManagerPropertyUpdater.java:56)
   at com.facebook.react.uimanager.ViewManager.updateProperties(ViewManager.java:48)

... `

Caused by java.lang.RuntimeException: Native object's weak_ptr for N6yandex4maps6mapkit3map9MapObjectE has expired

   at com.yandex.mapkit.map.internal.PlacemarkMapObjectBinding.setGeometry(PlacemarkMapObjectBinding.java)
   at ru.vvdev.yamap.view.YamapMarker.updateMarker(YamapMarker.java:87)
   at ru.vvdev.yamap.view.YamapMarker.setPoint(YamapMarker.java:57)
   at ru.vvdev.yamap.YamapMarkerManager.setPoint(YamapMarkerManager.java:58)
   at java.lang.reflect.Method.invoke(Method.java)

... `

Видео воспроизведения

https://user-images.githubusercontent.com/15292833/168269687-bff775d2-68b9-4ab9-b6d3-65ab5bea9878.mp4

ch3rn1k commented 2 years ago

+1, аналогичная проблема на Android. Под IOS такого не происходит.

DazzlingFame commented 2 years ago

Мб связано вот с этим issue https://github.com/yandex/mapkit-android-demo/issues/73

ch3rn1k commented 2 years ago

Мб связано вот с этим issue yandex/mapkit-android-demo#73

Уже натыкался на это и судя по ошибке, частично это оно и есть, но вот что с этим делать...

ch3rn1k commented 2 years ago

Также есть олд ишью, где парень радикально решил проблему просто отключив child у Marker :(

57

ch3rn1k commented 2 years ago

UPD: костыль - зачищать стейт из которого рисуются метки, пока что лучше ничего не придумал.

ownikss commented 2 years ago

Думаю в этой функции нужно добавить проверок на null, чтобы исправить проблему. Возможно есть какая-то функция isValid, для объектов MapKit. Постараемся проверить этот блок и исправить проблему. Есть ли фрагмент кода, на котором проблема воспроизводится стабильно?

ch3rn1k commented 2 years ago

Думаю в этой функции нужно добавить проверок на null, чтобы исправить проблему. Возможно есть какая-то функция isValid, для объектов MapKit. Постараемся проверить этот блок и исправить проблему. Есть ли фрагмент кода, на котором проблема воспроизводится стабильно?

Проверку на isValid() добавил и это теперь исправило вылет, но мне кажется что просто сброс апдейта это как то "жестко". Может быть надо если не валидный объект - удалять его как то?

ownikss commented 2 years ago

Тут суть в том что он пытается обновить маркер который уже удален, потому что его children тоже удалился. Дальше обновлений не будет и сборщик мусора удалит этот маркер. Возможно тут можно провести рефакторинг, но пока нет возможности

Все изменения я смержил в мастер. Новую версию в npm пока выкатить не могу - не приходит письмо с двухфакторкой. Надеюсь это сбой на стороне npm а не умышленная блокировка.

Для установки версии с фиксом можно использовать коммит bdce4a38eca076729c3334d1023cd684966a6c6f чтобы зафиксировать версию. Надеюсь проблему с npm получится решить

ownikss commented 2 years ago

Исправлено в версии 4.0.6

DazzlingFame commented 2 years ago

@ownikss @ch3rn1k обновился до "react-native-yamap": "4.0.7", проблема осталась. Падает с тем же сценарием, но с другой ошибкой:

Fatal Exception: java.lang.RuntimeException: Failed to remove MapObject

стектрейс:

at com.yandex.mapkit.map.internal.MapObjectCollectionBinding.remove(MapObjectCollectionBinding.java)
       at ru.vvdev.yamap.view.YamapView.removeChild(YamapView.java:539)
       at ru.vvdev.yamap.YamapViewManager.removeViewAt(YamapViewManager.java:240)
       at ru.vvdev.yamap.YamapViewManager.removeViewAt(YamapViewManager.java:26)
       at com.facebook.react.uimanager.NativeViewHierarchyManager.manageChildren(NativeViewHierarchyManager.java:460)
       at com.facebook.react.uimanager.UIViewOperationQueue$ManageChildrenOperation.execute(UIViewOperationQueue.java:217)

Попробую вот это решение https://github.com/yandex/mapkit-android-demo/issues/81

DazzlingFame commented 2 years ago

До чего пока дошёл:

ownikss commented 2 years ago

Смержено в 4.0.9

DazzlingFame commented 2 years ago

@ownikss @ch3rn1k
UPD:

с сожалению падения не пропали совсем, хоть их и стало где-то 1 падение на 20 ререндеров вместо 1 падения на 2 ререндера.

Стектрейс и ошибка такие же:

Периодически падает с

Fatal Exception: java.lang.RuntimeException: Failed to remove MapObject
 at com.yandex.mapkit.map.internal.MapObjectCollectionBinding.remove(MapObjectCollectionBinding.java)
       at ru.vvdev.yamap.view.YamapView.removeChild(YamapView.java:554)
       at ru.vvdev.yamap.YamapViewManager.removeViewAt(YamapViewManager.java:252)
       at ru.vvdev.yamap.YamapViewManager.removeViewAt(YamapViewManager.java:26)
       at com.facebook.react.uimanager.NativeViewHierarchyManager.manageChildren(NativeViewHierarchyManager.java:460)

Периодически с

Fatal Exception: java.lang.RuntimeException: Native object's weak_ptr for N6yandex4maps6mapkit3map9MapObjectE has expired
at com.yandex.mapkit.map.internal.MapObjectBinding.getParent(MapObjectBinding.java)
       at ru.vvdev.yamap.view.YamapView.removeChild(YamapView.java:554)
       at ru.vvdev.yamap.YamapViewManager.removeViewAt(YamapViewManager.java:252)
       at ru.vvdev.yamap.YamapViewManager.removeViewAt(YamapViewManager.java:26)
       at com.facebook.react.uimanager.NativeViewHierarchyManager.manageChildren(NativeViewHierarchyManager.java:460)

Судя по всему это то же самое место с removeChild. Продолжаю пробовать копать. Попробовал сохранять в переменные MapObject child и parent и проверять их на != null и isValid, это не помогло

Предлагаю переоткрыть issue, тк проблема осталась

DazzlingFame commented 2 years ago

Пример кода в removeChild, с которым падает

    public void removeChild(int index) {
        if (index < childs.size()) {
            ReactMapObject child = childs.remove(index);
            if (child != null) {
                MapObject childMapObject = child.getMapObject();
                if (childMapObject.isValid()) {
                    MapObjectCollection parent = childMapObject.getParent();
                    if (parent.isValid() && childMapObject.isValid()) {
                        parent.remove(childMapObject);
                    }
                }
            }
        }
    }
ch3rn1k commented 2 years ago

Пример кода в removeChild, с которым падает

    public void removeChild(int index) {
        if (index < childs.size()) {
            ReactMapObject child = childs.remove(index);
            if (child != null) {
                MapObject childMapObject = child.getMapObject();
                if (childMapObject.isValid()) {
                    MapObjectCollection parent = childMapObject.getParent();
                    if (parent.isValid() && childMapObject.isValid()) {
                        parent.remove(childMapObject);
                    }
                }
            }
        }
    }

Могу ошибаться, но как то странно, что childs.remove(index) присваивается в переменную, хотя это же вроде метод удаления элемента массива по индексу, хм

DazzlingFame commented 2 years ago

Пример кода в removeChild, с которым падает

    public void removeChild(int index) {
        if (index < childs.size()) {
            ReactMapObject child = childs.remove(index);
            if (child != null) {
                MapObject childMapObject = child.getMapObject();
                if (childMapObject.isValid()) {
                    MapObjectCollection parent = childMapObject.getParent();
                    if (parent.isValid() && childMapObject.isValid()) {
                        parent.remove(childMapObject);
                    }
                }
            }
        }
    }

Могу ошибаться, но как то странно, что childs.remove(index) присваивается в переменную, хотя это же вроде метод удаления элемента массива по индексу, хм

Это нормально, в java у массивов remove возвращает объект, который ремувнули

DazzlingFame commented 2 years ago

@ownikss @ch3rn1k там вышла новая версия мапкита https://yandex.ru/dev/maps/mapkit/doc/dg/concepts/versions.html в изменениях есть пункт Оптимизировано потребление памяти.

В целом, вполне вероятно, что там есть проблемы с памятью при рендере большого числа маркеров-компонентов и из-за них прилага и падает, так что мб имеет смысл попробовать обновиться.

P.S. Вчера делал ещё подход к починке removeChild:

Ничего не помогло