Проблема с жизненным циклом MvpDelegate
ermac95 opened this issue · comments
Добрый день. На большом количестве устройств - от 9 до 12 версии андроид происходит вылет со следующей ошибкой
Fatal Exception: java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.next(ArrayList.java:860)
at moxy.MvpDelegate.onAttach(MvpDelegate.java:166)
at moxy.MvpAppCompatFragment.onStart(MvpAppCompatFragment.java:38)
at androidx.fragment.app.Fragment.performStart(Fragment.java:2731)
at androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:365)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1206)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1368)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1446)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1509)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2637)
at androidx.fragment.app.FragmentManager.dispatchStart(FragmentManager.java:2595)
at androidx.fragment.app.Fragment.performStart(Fragment.java:2740)
at androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:365)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1206)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1368)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1446)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1509)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2637)
at androidx.fragment.app.FragmentManager.dispatchStart(FragmentManager.java:2595)
at androidx.fragment.app.FragmentController.dispatchStart(FragmentController.java:258)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:550)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:210)
at moxy.MvpAppCompatActivity.onStart(MvpAppCompatActivity.java:32)
at modulbank.ru.commonapp.ui.main.MainActivity.onStart(MainActivity.kt:274)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1456)
at android.app.Activity.performStart(Activity.java:8076)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3665)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2215)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:346)
at android.os.Looper.loop(Looper.java:475)
at android.app.ActivityThread.main(ActivityThread.java:7889)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1009)
Экран приложения, где происходит вылет не определен, крашлитика firebase выдает ошибку в вышеприведенном виде. Я так понимаю происходит рассинхронизация методов, в ходе чего происходит одновременная запись и чтение из листа, в связи с чем случается падение.
Привет! Скорей всего причина в следующем:
- Во вью стейт летит какая-то команда
- Эта команда сохраняется в очереди команд
- Эта команда как-то отрабатывает (на этом этапе не важно как)
- Затем происходит пересоздание Activity (или что вы используете)
- Мокси начинает итерацию команд, последовательно выполняя их
- Видимо, в момент вызова какой-то команды на View улетает событие в презентер, чем триггерится новая команда для View
- Новая команда добавляется в очередь команд View State
- Восстановление стейта продолжается, итератор пытается взять следующую команду, но обнаруживает, что список был изменен
- Происходит падение.
Надеюсь, понятно объяснил. Если нет, то спрашивай, что именно не понятно =)
В самой мокси нет никакой многопоточности, и всё вообще работает обычно на одном потоке - на main потоке. Так что проблема именно с модификацией списка команд в процесс итерирования по этому же самому списку.
Все понятно, хорошая мысль, спасибо за наводку. А есть какой нибудь способ отследить, где именно происходит подобная ситуация? чтобы понимать где искать, приложение коммерческое и масштабное, просто так перекапывать все команды всех вью не вариант конечно )
Тут обычно сложность в том, что проблема получается глубинная. Потому что выходит так, что View управляет доменом, а должно быть наоборот =(
Чаще всего это конечно какие-нибудь переключения свитчеров или изменение edit text, от которых отрабатывает какой-то listener, который пушит что-то в presenter, а presenter сразу начинает что-то делать. Например, показывать лоадинг.
Тут нет серебряной пули. Поможет только ручной поиск проблемы.
Как вариант - можно попробовать каким-нибудь regexp поменять код так, что перед каждым вызововм какого-нибудь метода viewState, делать проверку:
if (isInRestoreState()) {
throw RuntimeException("Wrong moxy usages")
}
А затем запустить приложение, заполнить как-нибудь view state и попересоздавать активити. И можно будет понять, кто ломает flow. Но это костыль.
Как мысль, можно это добавить в кодогенерацию, чтобы в будущем было понятно, какой метод ломает код. Потому что ConcurrentModificationException очень уж не информативен. Но это нужно сперва обмозговать.
Окей, спасибо большое за помощь, буду разбираться )