Android 后台管理:仅在所有者';s初始化阶段

Android 后台管理:仅在所有者';s初始化阶段,android,android-fragments,kotlin,back-stack,fragmentmanager,Android,Android Fragments,Kotlin,Back Stack,Fragmentmanager,我在MainActivity中使用底部导航栏来处理一些片段。这是用于在它们之间切换的代码: private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item -> if (item.isChecked && supportFragmentManager.findFragmentById(R.id.ac

我在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.java:59) 位于androidx.fragment.app.fragment.performCreate(fragment.java:2580) 位于androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:837) 在androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1237) 位于androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1302) 位于androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439) 位于androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2075) 位于androidx.fragment.app.FragmentManagerImpl.ExecuteOptoGether(FragmentManagerImpl.java:1865) 在androidx.fragment.app.FragmentManagerImpl.RemovedAndTopOperationsAndExecute(FragmentManagerImpl.java:1820) 位于androidx.fragment.app.FragmentManagerImpl.ExecutPendingActions(FragmentManagerImpl.java:1726) 位于androidx.fragment.app.FragmentManagerImpl$2.run(FragmentManagerImpl.java:150) 位于android.os.Handler.handleCallback(Handler.java:789) 位于android.os.Handler.dispatchMessage(Handler.java:98) 位于android.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毫秒的延迟来解决这个问题,因为我无法找到其他解决方案

请询问您是否需要有关此的更多详细信息。 提前谢谢你

可能的临时解决方案

编辑 我解决了这个问题,将app-compat依赖性降级为
androidx.appcompat:appcompat:1.0.2
,但这只是一个临时解决方案,因为我将来必须更新它。我希望有人能解决这个问题

编辑2 我通过从片段事务中删除setTransition()解决了这个问题。至少我知道android应用程序通常没有良好转换的原因

编辑3 如果您使用的是“androidx.core:core ktx:1.0.2”,那么使用ViewPager处理底部栏导航可能是避免此问题并使事情顺利进行的最佳解决方案, 尝试更改为1.0.1

如果您正在使用lifecycle(或rxFragment)和androidx_appcompat:alpha05,请尝试更改版本。
ex)appcompat:1.1.0-beta01或1.0.2


我认为当目标片段被重用(onPause onResume)时,保存状态时会出现错误。

我将实现更改为androidx的api。appcompat:appcompat:1.0.2,它对我有效

,因为版本1.0.0没有检查状态,所以它不会引发异常, 但是版本1.1.0更改了源代码,因此抛出了异常

这是片段版本-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);
        }

我推荐第一种方法,因为第二种方法是跟踪源代码,如果源代码
代码更改代码,它将无效。

此错误似乎可以通过使用androidx解决。appcompat:appcomat:1.1.0-rc01和androidx.fragment:fragment:1.1.0-rc03


我也有同样的问题

val fragment = Account.activityAfterLogin
        val ft = activity?.getSupportFragmentManager()?.beginTransaction()
        //error
        ft?.setCustomAnimations(android.R.anim.slide_in_left,android.R.anim.slide_out_right)0
        ft?.replace(R.id.framelayout_account,fragment)
        ft?.commit()
更改库版本没有帮助。 我通过在
ft?.setCustomAnimations()
方法之后添加
ft?.AddToBackStack(null)
行来解决这个问题,就是这样。
动画工作正常,没有崩溃。

我在使用
setCustomAnimations
时遇到这个问题。 通过删除
setCustomAnimations
解决了我的问题。 即使使用setCustomAnimation,在显示片段之前创建片段的新实例也没有问题


编辑:另一种方法是将片段添加到backstack。

如果可以的话,我在BottomNavigationView和setCustomAnimations中遇到了相同的问题,基本上是通过在片段之间快速切换,您可能会在前一个片段未完成时启动片段事务,然后崩溃

public void toggleNavigationBarItems(boolean enabled) {
   Menu navMenu = navigationView.getMenu();
   for (int i = 0; i < navMenu.size(); ++i) {
       navMenu.getItem(i).setEnabled(enabled);
   }
}
@Nullable
@Override
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
    if(enter){ // check the note below
        Animator animator = AnimatorInflater.loadAnimator(getContext(), nextAnim);
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                myActivity.toggleNavigationBarItems(true)
            }
        });
        return animator;
    }
    return super.onCreateAnimator(transit, enter, nextAnim);
}
if (getChildFragmentManager().getBackStackEntryCount() > 0) {
    getChildFragmentManager().popBackStack();
}

FragmentTransaction addTransaction = getChildFragmentManager().beginTransaction();
addTransaction.setCustomAnimations(R.animator.fragment_fade_in, R.animator.fragment_fade_out);
addTransaction.addToBackStack(null);
addTransaction.add(R.id.frame, fragment, fragment.getClass().getName()).commitAllowingStateLoss();
public void addFragment(int contentFrameId, Fragment fragment, Bundle param, boolean addToStack) {
    try {
        if (!fragment.isAdded()) {
            if (param != null) {
                fragment.setArguments(param);
            }

            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
                    .add(contentFrameId, fragment)
                    .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);

            if (addToStack)
                fragmentTransaction.addToBackStack(fragment.getClass().toString());

            fragmentTransaction.commit();
        }
    } catch (IllegalStateException e) {
        handleError(e.getMessage());
    } catch (Exception e) {
        handleError(e.getMessage());
    }
}
public synchronized void addFragment(int contentFrameId, Fragment fragment, Bundle param, boolean addToStack) {
    try {
        if (!fragment.isAdded()) {
            if (param != null) {
                fragment.setArguments(param);
            }

            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
                    .add(contentFrameId, fragment)
                    .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);

            if (addToStack)
                fragmentTransaction.addToBackStack(fragment.getClass().toString());

            fragmentTransaction.commit();
        }
    } catch (IllegalStateException e) {
        handleError(e.getMessage());
    } catch (Exception e) {
        handleError(e.getMessage());
    }
}