Управление Backstack: Restarter должен быть создан только на этапе инициализации владельца - PullRequest
3 голосов
/ 11 июня 2019

Я использую нижнюю панель навигации в своем MainActivity для обработки некоторых фрагментов.Это код, используемый для переключения между ними:

private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
    if (item.isChecked &&
        supportFragmentManager.findFragmentById(R.id.act_main_fragment_container) != null
    )
        return@OnNavigationItemSelectedListener false
    val fragment =
        when (item.itemId) {
            R.id.navigation_home      -> fragments[0]
            R.id.navigation_bookings  -> fragments[1]
            R.id.navigation_messages  -> fragments[2]
            R.id.navigation_dashboard -> fragments[3]
            R.id.navigation_profile   -> fragments[4]
            else                      -> fragments[0]
        }
    this replaceWithNoBackStack fragment
    return@OnNavigationItemSelectedListener true
}

метод replaceWithNoBackstack - просто сокращение для этого:

supportFragmentManager
    ?.beginTransaction()
    ?.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
    ?.replace(containerId, fragment)
    ?.commit()

Проблема заключается в том, что когда я переключаюсь быстрее между нимимое приложение аварийно завершает работу со следующим исключением:

java.lang.IllegalStateException: Рестартер должен быть создан только на этапе инициализации владельца в androidx.savedstate.SavedStateRegistryController.performRestore (SavedStateRegistryController.javax59).fragment.app.androidx.fragment.app.на андроидx.fragment.app.на androidx.fragment.app.FragmentManagerImpl $ 2.run (FragmentManagerImpl.java:150) на android.os.Handler.handleCallback (Handler.java:789) на android.os.Handler.dispatchMessage (Handler.java:98) на андроиде.os.Looper.loop (Looper.java:164) на android.app.ActivityThread.main (ActivityThread.java:6709) на java.lang.reflect.Method.invoke (собственный метод) на com.android.internal.os.Zygote $ MethodAndArgsCaller.run (Zygote.java:240) на com.android.internal.os.ZygoteInit.main (ZygoteInit.java:769) Я много искал и не смог найти ответ.

Я также получил эту ошибку, если я делаю вызов API, помещаю приложение в фоновый режим, жду ответа, и когда я возвращаюсь к приложению, приложение падаетКроме того, я пытаюсь немедленно отобразить фрагмент диалога (причина, по которой я думаю, что это происходит, заключается в том, что операция по воссозданию фрагмента при возврате из фона все еще выполняется во время отображения фрагмента диалога).Я решил это хакерским способом, установив задержку в 500 мс для диалога, потому что я не мог найти другие решения.

Пожалуйста, спросите, нужны ли вам дополнительные подробности относительно этого.Заранее спасибо!

РЕДАКТИРОВАТЬ Я решил эту проблему, снизив зависимость совместимости приложения до androidx.appcompat:appcompat:1.0.2, но это всего лишь временное решение, поскольку мне придется обновлять его в будущем.Я надеюсь, что кто-то поймет это.

EDIT 2 Я решил проблему, удалив setTransition () из транзакций фрагментов.По крайней мере, я знаю причину, почему приложения для Android не имеют хорошие переходы в целом

Ответы [ 4 ]

0 голосов
/ 25 июля 2019

Эта ошибка, похоже, устраняется с помощью androidx.appcompat: appcomat: 1.1.0-rc01 и androidx.fragment: фрагмент: 1.1.0-rc03

https://developer.android.com/jetpack/androidx/releases/fragment#1.1.0-rc03

0 голосов
/ 21 июня 2019

Я изменил реализацию на api для androidx.appcompat: appcompat: 1.0.2, и он работал для меня

0 голосов
/ 27 июня 2019

потому что версия 1.0.0 не проверяет состояние, поэтому не будет выдавать исключение, но версия 1.1.0 изменяет исходный код, поэтому возникает исключение.

это исходный код Fragment version-1.1.0, он вызовет метод performRestore

    void performCreate(Bundle savedInstanceState) {
        if (mChildFragmentManager != null) {
            mChildFragmentManager.noteStateNotSaved();
        }
        mState = CREATED;
        mCalled = false;
        mSavedStateRegistryController.performRestore(savedInstanceState);
        onCreate(savedInstanceState);
        mIsCreated = true;
        if (!mCalled) {
            throw new SuperNotCalledException("Fragment " + this
                    + " did not call through to super.onCreate()");
        }
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
    }

/**
the exception
**/
public void performRestore(@Nullable Bundle savedState) {
        Lifecycle lifecycle = mOwner.getLifecycle();
        if (lifecycle.getCurrentState() != Lifecycle.State.INITIALIZED) {
            throw new IllegalStateException("Restarter must be created only during "
                    + "owner's initialization stage");
        }
        lifecycle.addObserver(new Recreator(mOwner));
        mRegistry.performRestore(lifecycle, savedState);
    }

это исходный код версии 1.0.0 , не вызывал performRestore, поэтому не будет выбрасывать исключение

void performCreate(Bundle savedInstanceState) {
    if (mChildFragmentManager != null) {
        mChildFragmentManager.noteStateNotSaved();
    }
    mState = CREATED;
    mCalled = false;
    onCreate(savedInstanceState);
    mIsCreated = true;
    if (!mCalled) {
        throw new SuperNotCalledException("Fragment " + this
                + " did not call through to super.onCreate()");
    }
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}

Существует два разных решения , которые могут справиться с этим:
Первое решение - разделить транзакцию。
Потому что мы всегда используем replace или объединяем remove и add в одну транзакцию. Мы можем разделить транзакцию на две транзакции следующим образом:

FragmentTransaction ft = manager.beginTransaction();
        Fragment prev = manager.findFragmentByTag(tag);
        if (prev != null) {
        //commit immediately
            ft.remove(prev).commitAllowingStateLoss();
        }
        FragmentTransaction addTransaction = manager.beginTransaction();
        addTransaction.addToBackStack(null);
        addTransaction.add(layoutId, fragment,
                tag).commitAllowingStateLoss();

потому что эти две транзакции будут двумя разными сообщениями, которые будут обработаны обработчиком.
Вторым решением является предварительная проверка состояния. мы можем следовать исходному коду , проверить состояние заранее

FragmentTransaction ft = manager.beginTransaction();
        Fragment prev = manager.findFragmentByTag(tag);
        if (prev != null) {
        if (prev.getLifecycle().getCurrentState() != Lifecycle.State.INITIALIZED) {
            return;
        }
            ft.remove(prev);
        }

Я рекомендую первый способ, потому что второй способ заключается в следующем, если исходный код изменить код, он будет недействительным。

0 голосов
/ 12 июня 2019

Если вы используете 'androidx.core: core-ktx: 1.0.2', попробуйте изменить на 1.0.1

Если вы используете жизненный цикл (или rxFragment)androidx_appcompat: alpha05, попробуйте изменить версию.
ex) appcompat: 1.1.0-beta01 или 1.0.2

Я думаю, что это выглядит как ошибка при сохранении состояния при повторном использовании целевого фрагмента (OnPause-onResume).

...