我从我的应用程序中获取用户报告,提供以下例外:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)
显然它与FragmentManager有关,我不使用它。 stacktrace没有显示我自己的任何类,所以我不知道这个异常发生在哪里以及如何防止它。
对于记录:我有一个tabhost,并且在每个选项卡中都有一个ActivityGroup在活动之间切换。
答案 0 :(得分:673)
请检查我的回答here。基本上我只需要:
@Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
请勿对super()
方法拨打saveInstanceState
。这搞砸了......
这是支持包中的已知bug。
如果您需要保存实例并向outState
Bundle
添加内容,可以使用以下内容:
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
super.onSaveInstanceState(outState);
}
最后使用正确的解决方案(如评论中所示):
transaction.commitAllowingStateLoss();
添加或执行导致FragmentTransaction
的{{1}}。
答案 1 :(得分:114)
类似的错误消息存在许多相关问题。检查此特定堆栈跟踪的第二行。此例外特别与对FragmentManagerImpl.popBackStackImmediate
的调用有关。
如果已保存会话状态,此方法调用(如popBackStack
)将始终失败并显示IllegalStateException
。检查来源。没有什么可以阻止抛出此异常。
super.onSaveInstanceState
的通话无济于事。 commitAllowingStateLoss
创建片段无济于事。以下是我观察问题的方法:
onSaveInstanceState
。popBackStackImmediate
。IllegalStateException
被抛出。以下是我为解决问题所做的工作:
因为无法避免回调中的IllegalStateException
,所以赶上&忽略它。
try {
activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
// There's no way to avoid getting this if saveInstanceState has already been called.
}
这足以阻止应用崩溃。但现在用户将恢复该应用程序并看到他们认为按下的按钮根本没有被按下(他们认为)。表单片段仍在显示!
要解决此问题,请在创建对话框时,进行一些状态以指示进程已开始。
progressDialog.show(fragmentManager, TAG);
submitPressed = true;
并将此状态保存在捆绑包中。
@Override
public void onSaveInstanceState(Bundle outState) {
...
outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}
请勿忘记在onViewCreated
然后,在恢复时,如果先前尝试过提交,则回滚片段。这可以防止用户回到看似未提交的表单。
@Override
public void onResume() {
super.onResume();
if (submitPressed) {
// no need to try-catch this, because we are not in a callback
activity.getSupportFragmentManager().popBackStackImmediate(name);
submitPressed = false;
}
}
答案 2 :(得分:53)
在显示片段之前检查活动isFinishing()
,并注意commitAllowingStateLoss()
。
示例:
if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
DummyFragment dummyFragment = DummyFragment.newInstance();
ft.add(R.id.dummy_fragment_layout, dummyFragment);
ft.commitAllowingStateLoss();
}
答案 3 :(得分:21)
2017年10月,谷歌制作了Android支持库,新的东西称为Lifecycle组件。它为“在onSaveInstanceState之后无法执行此操作”问题提供了一些新的想法。
简而言之:
更长版本的解释:
为什么会出现这个问题?
这是因为你试图从你的活动中使用FragmentManager
(我认为这将保留你的片段?)为你的片段提交一个事务。通常这看起来就像你正在尝试为即将到来的片段做一些事务,同时主机活动已经调用savedInstanceState
方法(用户可能碰巧触摸主页按钮,因此活动调用onStop()
,我的情况就是这个原因)
通常这个问题不应该发生 - 我们总是尝试在最开始时将片段加载到活动中,就像onCreate()
方法是一个完美的地方。但有时这确实发生,尤其是当您无法确定要加载到该活动的片段时,或者您正试图从AsyncTask
块加载片段时(或者任何事情都需要很少的时间)。在片段事务真正发生之前的时间,但在活动的onCreate()
方法之后,用户可以做任何事情。如果用户按下主页按钮(触发活动的onSavedInstanceState()
方法),则会发生can not perform this action
崩溃。
如果有人想深入了解此问题,建议他们查看此博客post。它深入了解源代码层并对其进行了大量解释。此外,它给出了您不应该使用commitAllowingStateLoss()
方法来解决此崩溃的原因(相信我它对您的代码没有任何好处)
如何解决此问题?
我应该使用commitAllowingStateLoss()
方法加载片段吗? 不,你不应该;
我应该覆盖onSaveInstanceState
方法,忽略其中的super
方法吗? 不,你不应该;
我是否应该使用神奇的isFinishing
内部活动来检查主机活动是否适合片段交易?是的,看起来像正确的做法。
看看Lifecycle组件可以做什么。
基本上,Google在AppCompatActivity
类(以及您应该在项目中使用的其他几个基类)中进行了一些实现,这使得更容易确定当前的生命周期状态。回顾一下我们的问题:为什么会出现这个问题?这是因为我们在错误的时间做某事。所以我们尽量不这样做,这个问题就会消失。
我为自己的项目编写了一些代码,这是我使用LifeCycle
执行的操作。我在Kotlin编码。
val hostActivity: AppCompatActivity? = null // the activity to host fragments. It's value should be properly initialized.
fun dispatchFragment(frag: Fragment) {
hostActivity?.let {
if(it.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)){
showFragment(frag)
}
}
}
private fun showFragment(frag: Fragment) {
hostActivity?.let {
Transaction.begin(it, R.id.frag_container)
.show(frag)
.commit()
}
如上所示。我将检查主机活动的生命周期状态。借助支持库中的Lifecycle组件,这可能更具体。代码lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)
表示,如果当前状态至少为onResume
,不迟于它?这确保我的方法不会在其他生命状态期间执行(如onStop
)。
这一切都完成了吗?
当然不是。我展示的代码告诉了一些防止应用程序崩溃的新方法。但如果确实进入状态onStop
,则该行代码不会执行任何操作,因此屏幕上不会显示任何内容。当用户返回应用程序时,他们将看到一个空屏幕,这是空主机活动,根本没有显示任何片段。这是糟糕的经历(是的,比崩溃好一点)。
所以在这里我希望有更好的东西:如果生命状态晚于onResume
,应用程序不会崩溃,事务方法是生命状态感知;此外,在用户回到我们的应用程序之后,活动将尝试继续完成该片段事务操作。
我在此方法中添加了更多内容:
class FragmentDispatcher(_host: FragmentActivity) : LifecycleObserver {
private val hostActivity: FragmentActivity? = _host
private val lifeCycle: Lifecycle? = _host.lifecycle
private val profilePendingList = mutableListOf<BaseFragment>()
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun resume() {
if (profilePendingList.isNotEmpty()) {
showFragment(profilePendingList.last())
}
}
fun dispatcherFragment(frag: BaseFragment) {
if (lifeCycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true) {
showFragment(frag)
} else {
profilePendingList.clear()
profilePendingList.add(frag)
}
}
private fun showFragment(frag: BaseFragment) {
hostActivity?.let {
Transaction.begin(it, R.id.frag_container)
.show(frag)
.commit()
}
}
}
我在这个dispatcher
类中维护一个列表,以存储那些没有机会完成事务操作的片段。当用户从主屏幕返回并发现仍有片段等待启动时,它将转到resume()
注释下的@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
方法。现在我认为它应该像我预期的那样工作。
答案 4 :(得分:21)
以下是此问题的不同解决方案。
使用私有成员变量,您可以将返回的数据设置为intent,然后可以在super.onResume()之后处理;
像这样:
private Intent mOnActivityResultIntent = null;
@Override
protected void onResume() {
super.onResume();
if(mOnActivityResultIntent != null){
... do things ...
mOnActivityResultIntent = null;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(data != null){
mOnActivityResultIntent = data;
}
}
答案 5 :(得分:16)
简短而有效的解决方案:
遵循简单步骤
步骤
步骤1:覆盖相应片段中的onSaveInstanceState
状态。并从中删除超级方法。
@Override
public void onSaveInstanceState( Bundle outState ) {
}
第2步:使用
fragmentTransaction.commitAllowingStateLoss( );
而不是fragmentTransaction.commit( );
。
答案 6 :(得分:12)
请注意,使用java.util.List<String> list = new java.util.ArrayList<>();
// equals:
import java.util.List;
import java.util.ArrayList;
List<String> list = new ArrayList<>(); // much cleaner option
可能会导致用户体验不佳。有关抛出此异常的原因的详细信息,请参阅this post。
答案 7 :(得分:10)
我找到了解决这类问题的肮脏方案。如果您仍然希望保留ActivityGroups
因为任何原因(我有时间限制的原因),您只需实施
public void onBackPressed() {}
在Activity
中,并在其中执行一些back
代码。即使旧设备上没有这样的方法,这个方法也会被新方法调用。
答案 8 :(得分:6)
不要使用commitAllowingStateLoss(),它只应用于UI状态可以在用户上意外更改的情况。
如果事务发生在parentFragment的ChildFragmentManager中,请使用 parentFragment.isResume()在外面检查。
if (parentFragment.isResume()) {
DummyFragment dummyFragment = DummyFragment.newInstance();
transaction = childFragmentManager.BeginTransaction();
trans.Replace(Resource.Id.fragmentContainer, startFragment);
}
答案 9 :(得分:5)
我有类似的问题,情况是这样的:
活动的 onCreate 方法如下:
mMainFragment = (SelectionFragment) getSupportFragmentManager()
.findFragmentByTag(MAIN_FRAGMENT_TAG);
if (mMainFragment == null) {
mMainFragment = new SelectionFragment();
mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
R.layout.item_main_menu, getResources().getStringArray(
R.array.main_menu)));
mMainFragment.setOnSelectionChangedListener(this);
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
transaction.commit();
}
抛出异常是因为when配置更改(设备已旋转),活动已创建,主片段从片段管理器的历史记录中检索,同时片段已经具有 OLD 对已销毁活动
的引用将实现更改为此解决了问题:
mMainFragment = (SelectionFragment) getSupportFragmentManager()
.findFragmentByTag(MAIN_FRAGMENT_TAG);
if (mMainFragment == null) {
mMainFragment = new SelectionFragment();
mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
R.layout.item_main_menu, getResources().getStringArray(
R.array.main_menu)));
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
transaction.commit();
}
mMainFragment.setOnSelectionChangedListener(this);
您需要在每次创建活动时设置侦听器,以避免碎片引用旧活动的已销毁实例。
答案 10 :(得分:3)
当我按下按钮取消我的地图片段活动上的意图选择器时,我得到了这个异常。 我通过将onResume(我正在初始化片段)的代码替换为onstart()来解决这个问题,并且应用程序正常工作。希望它有所帮助。
答案 11 :(得分:2)
此崩溃是由于在其自己的Activity的生命周期已经在SaveInstanceState上运行之后提交了FragmentTransaction。这通常是由于从异步回调中提交FragmentTransactions引起的。请查看链接的资源以获取更多详细信息。
碎片交易和活动状态丢失
http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
答案 12 :(得分:2)
如果您继承自FragmentActivity
,则必须在onActivityResult()
中调用超类:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
...
}
如果您不这样做,并尝试在该方法中显示一个片段对话框,则可能会得到OP的IllegalStateException
。 (说实话,我不太理解为什么为什么超级调用可以解决此问题。onActivityResult()
在onResume()
之前被调用,因此仍然不允许显示片段对话框。)
答案 13 :(得分:2)
如果你在onActivityResult中做了一些FragmentTransaction,你可以在onActivityResult中设置一些布尔值,然后在onResume中你可以根据布尔值进行FragmentTransaction。请参考以下代码。
@Override
protected void onResume() {
super.onResume;
if(isSwitchFragment){
isSwitchFragment=false;
bottomNavigationView.getTabAt(POS_FEED).select();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == FilterActivity.FILTER_REQUEST_EVENT && data != null) {
isSwitchFragment=true;
}
}
答案 14 :(得分:2)
我认为使用transaction.commitAllowingStateLoss();
并非最佳解决方案。
当活动的配置发生更改并调用片段onSavedInstanceState()
,然后您的异步回调方法尝试提交片段时,将抛出此异常。
简单的解决方案可以检查活动是否正在改变配置
e.g。检查isChangingConfigurations()
即
if(!isChangingConfigurations()) {
//commit transaction.
}
结帐this链接
答案 15 :(得分:1)
礼貌:Solution for IllegalStateException
这个问题困扰了我很多时间,但是幸运的是我提供了一个具体的解决方案。其详细说明是here。
使用commitAllowStateloss()可能会阻止此异常,但会导致UI异常。到目前为止,我们已经了解到,当我们在Activity状态丢失后尝试提交片段时会遇到IllegalStateException-因此我们应该将事务延迟到状态已恢复。可以像这样简单地完成
声明两个私有布尔变量
public class MainActivity extends AppCompatActivity {
//Boolean variable to mark if the transaction is safe
private boolean isTransactionSafe;
//Boolean variable to mark if there is any transaction pending
private boolean isTransactionPending;
现在在onPostResume()和onPause中,我们设置和取消设置布尔变量isTransactionSafe。想法是仅在活动处于前台时才将事务标记为安全,这样就不会造成状态损失。
/*
onPostResume is called only when the activity's state is completely restored. In this we will
set our boolean variable to true. Indicating that transaction is safe now
*/
public void onPostResume(){
super.onPostResume();
isTransactionSafe=true;
}
/*
onPause is called just before the activity moves to background and also before onSaveInstanceState. In this
we will mark the transaction as unsafe
*/
public void onPause(){
super.onPause();
isTransactionSafe=false;
}
private void commitFragment(){
if(isTransactionSafe) {
MyFragment myFragment = new MyFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.frame, myFragment);
fragmentTransaction.commit();
}
}
-到目前为止,我们将做的事情将保存在IllegalStateException中,但是如果在活动移至后台之后完成交易,我们的交易将丢失,类似于commitAllowStateloss()。为了解决这个问题,我们提供了isTransactionPending布尔变量
public void onPostResume(){
super.onPostResume();
isTransactionSafe=true;
/* Here after the activity is restored we check if there is any transaction pending from
the last restoration
*/
if (isTransactionPending) {
commitFragment();
}
}
private void commitFragment(){
if(isTransactionSafe) {
MyFragment myFragment = new MyFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.frame, myFragment);
fragmentTransaction.commit();
isTransactionPending=false;
}else {
/*
If any transaction is not done because the activity is in background. We set the
isTransactionPending variable to true so that we can pick this up when we come back to
foreground
*/
isTransactionPending=true;
}
}
答案 16 :(得分:1)
就我而言,我在名为onActivityResult的重写方法中遇到了此错误。挖完之后,我才发现也许我需要先叫“ super ”。
我添加了它,就可以了
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data); //<--- THIS IS THE SUPPER CALL
if (resultCode == Activity.RESULT_OK && requestCode == 0) {
mostrarFragment(FiltroFragment.newInstance())
}
}
也许您只需要在代码执行前的任何覆盖上添加一个“超级”即可。
答案 17 :(得分:1)
关于@Anthonyeef的最佳答案,这是Java中的示例代码:
private boolean shouldShowFragmentInOnResume;
private void someMethodThatShowsTheFragment() {
if (this.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
showFragment();
} else {
shouldShowFragmentInOnResume = true;
}
}
private void showFragment() {
//Your code here
}
@Override
protected void onResume() {
super.onResume();
if (shouldShowFragmentInOnResume) {
shouldShowFragmentInOnResume = false;
showFragment();
}
}
答案 18 :(得分:1)
在我的案例中,我发现的最顺畅和最简单的解决方案可能是避免将有问题的片段从堆栈中弹出以响应活动结果。因此,在onActivityResult()
:
popMyFragmentAndMoveOn();
到此:
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
popMyFragmentAndMoveOn();
}
}
在我的案例中有所帮助。
答案 19 :(得分:1)
要绕过此问题,我们可以使用The Navigation Architecture Component,它是Google I / O 2018中引入的。 导航体系结构组件简化了Android应用中导航的实现。
答案 20 :(得分:1)
每当您尝试在活动中加载片段时,请确保活动处于恢复状态且不会暂停状态。在暂停状态下,您可能最终失去已完成的提交操作。
您可以使用transaction.commitAllowingStateLoss()而不是transaction.commit()来加载片段
或
创建一个布尔值并检查活动是否不会暂停
@Override
public void onResume() {
super.onResume();
mIsResumed = true;
}
@Override
public void onPause() {
mIsResumed = false;
super.onPause();
}
然后加载片段检查
if(mIsResumed){
//load the your fragment
}
答案 21 :(得分:0)
我也遇到了这个问题,每当您FragmentActivity
的上下文发生变化(例如更改屏幕方向等)时,就会出现问题。因此,最好的解决方法是更新FragmentActivity
。
答案 22 :(得分:0)
我知道@Ovidiu Latcu已经接受了答案,但过了一段时间,错误仍然存在。
@Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
Crashlytics仍然向我发送这个奇怪的错误消息。
但是现在仅在版本7+(牛轧糖)上发生错误 我的修复是在fragmentTransaction中使用 commitAllowingStateLoss()而不是commit()。
这个post对commitAllowingStateLoss()有帮助,并且再也没有出现过片段问题。
总结一下,这里接受的答案可能适用于前Nougat Android版本。
这可能会节省一些人的搜索时间。 快乐的编码。 &lt; 3欢呼
答案 23 :(得分:0)
我遇到了完全相同的问题。 这是因为以前的活动遭到破坏。 当ı支持之前的活动时,它被销毁了。 我把它作为基础活动(错误)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SpinnerCustom2.setFragmentManager(getSupportFragmentManager());
onCreateDrawerActivity(savedInstanceState);
}
我把它放入onStart它是正确的
@Override
protected void onStart() {
super.onStart();
SpinnerCustom2.setFragmentManager(getSupportFragmentManager());
}
答案 24 :(得分:0)
另一种可行的解决方法,我不确定在所有情况下是否有帮助(来源here):
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final View rootView = findViewById(android.R.id.content);
if (rootView != null) {
rootView.cancelPendingInputEvents();
}
}
}
答案 25 :(得分:0)
我最终创建了一个基本片段并让我的应用中的所有片段扩展它
public class BaseFragment extends Fragment {
private boolean mStateSaved;
@CallSuper
@Override
public void onSaveInstanceState(Bundle outState) {
mStateSaved = true;
super.onSaveInstanceState(outState);
}
/**
* Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
* would otherwise occur.
*/
public void showAllowingStateLoss(FragmentManager manager, String tag) {
// API 26 added this convenient method
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (manager.isStateSaved()) {
return;
}
}
if (mStateSaved) {
return;
}
show(manager, tag);
}
}
然后当我尝试显示片段时,我使用showAllowingStateLoss
代替show
MyFragment.newInstance()
.showAllowingStateLoss(getFragmentManager(), MY_FRAGMENT.TAG);
我从这个PR来到这个解决方案:https://github.com/googlesamples/easypermissions/pull/170/files
答案 26 :(得分:0)
从支持库版本24.0.0开始,您可以调用同步提交此事务的FragmentTransaction.commitNow()
方法,而不是调用commit()
后跟executePendingTransactions()
。正如documentation所说,这种方法更好:
调用commitNow比调用commit()后跟executePendingTransactions()更好,因为后者的副作用是尝试提交所有当前挂起的事务,无论这是否是所需的行为。
答案 27 :(得分:0)
在您的活动中添加此内容
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (outState.isEmpty()) {
// Work-around for a pre-Android 4.2 bug
outState.putBoolean("bug:fix", true);
}
}
答案 28 :(得分:0)
如果您使用popBackStack()或popBackStackImmediate()方法崩溃,请尝试使用以下方法进行修复:
if (!fragmentManager.isStateSaved()) {
fragmentManager.popBackStackImmediate();
}
这对我也有用。
答案 29 :(得分:0)
@Gian Gomen以我为例,SUPER解决了问题。似乎比commitAllowingStateLoss()更正确的解决方案,因为它可以解决问题,而不是隐藏问题。
@Override
public void onRequestPermissionsResult(
final int requestCode,
@NonNull final String[] permissions,
@NonNull final int[] grantResults
) {
super.onRequestPermissionsResult(requestCode,permissions, grantResults); //<--- Without this line crash
switch (requestCode) {
case Constants.REQUEST_CODE_PERMISSION_STORAGE:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
onPermissionGranted(Constants.REQUEST_CODE_PERMISSION_STORAGE);
}
break;
}
答案 30 :(得分:0)
如果状态保存,则使用remove()而不是popup()。
private void removeFragment() {
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.isStateSaved()) {
List<Fragment> fragments = fragmentManager.getFragments();
if (fragments != null && !fragments.isEmpty()) {
fragmentManager.beginTransaction().remove(fragments.get(fragments.size() - 1)).commitAllowingStateLoss();
}
}
}
答案 31 :(得分:0)
片段事务不应在Activity.onStop()
之后执行!
检查在onStop()
之后没有任何可以执行事务的回调。最好解决原因,而不是尝试使用.commitAllowingStateLoss()
答案 32 :(得分:0)
Kotlin扩展
fun FragmentManager?.replaceAndAddToBackStack(
@IdRes containerViewId: Int,
fragment: () -> Fragment,
tag: String
) {
// Find and synchronously remove a fragment with the same tag.
this?.findFragmentByTag(tag)?.let {
beginTransaction().remove(it).commitNow()
}
// Add a fragment.
this?.beginTransaction()?.run {
replace(containerViewId, fragment, tag)
// The next line will add the fragment to a back stack.
// Remove if not needed.
addToBackStack(null)
}?.commitAllowingStateLoss()
}
用法:
val fragment = { SomeFragment.newInstance(data) }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, SomeFragment.TAG)
答案 33 :(得分:0)
此处引发异常(在FragmentActivity中):
@Override
public void onBackPressed() {
if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
super.onBackPressed();
}
}
在FragmentManager.popBackStatckImmediate()
中,首先调用FragmentManager.checkStateLoss()
。这是IllegalStateException
的原因。请参阅以下实施:
private void checkStateLoss() {
if (mStateSaved) { // Boom!
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
}
我只需使用标记来标记活动的当前状态即可解决此问题。这是我的解决方案:
public class MainActivity extends AppCompatActivity {
/**
* A flag that marks whether current Activity has saved its instance state
*/
private boolean mHasSaveInstanceState;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
mHasSaveInstanceState = true;
super.onSaveInstanceState(outState);
}
@Override
protected void onResume() {
super.onResume();
mHasSaveInstanceState = false;
}
@Override
public void onBackPressed() {
if (!mHasSaveInstanceState) {
// avoid FragmentManager.checkStateLoss()'s throwing IllegalStateException
super.onBackPressed();
}
}
}