82

对于我的一个片段,我的 onActivityResult 中有以下代码:

onActivityResult(int requestCode, int resultCode, Intent data){
   //other code
   ProgressFragment progFragment = new ProgressFragment();  
   progFragment.show(getActivity().getSupportFragmentManager(), PROG_DIALOG_TAG);
   // other code
}

但是,我收到以下错误:

Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState   

任何人都知道发生了什么,或者我该如何解决这个问题?我应该注意我正在使用 Android 支持包。

4

17 回答 17

76

如果您使用 Android 支持库,onResume 方法不是正确的地方,在哪里玩片段。您应该在 onResumeFragments 方法中执行此操作,请参阅 onResume 方法说明:http: //developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume%28%29

所以从我的角度来看,正确的代码应该是:

private boolean mShowDialog = false;

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
  super.onActivityResult(requestCode, resultCode, data);

  // remember that dialog should be shown
  mShowDialog = true;
}

@Override
protected void onResumeFragments() {
  super.onResumeFragments();

  // play with fragments here
  if (mShowDialog) {
    mShowDialog = false;

    // Show only if is necessary, otherwise FragmentManager will take care
    if (getSupportFragmentManager().findFragmentByTag(PROG_DIALOG_TAG) == null) {
      new ProgressFragment().show(getSupportFragmentManager(), PROG_DIALOG_TAG);
    }
  }
}
于 2013-04-04T03:38:34.923 回答
27

编辑:不是错误,而是片段框架中的更多缺陷。这个问题的更好答案是上面@Arcao 提供的答案。

---- 原帖----

实际上这是支持包的一个已知错误(编辑:实际上不是错误。请参阅@alex-lockwood 的评论)。在错误报告的评论中发布的解决方法是修改 DialogFragment 的源,如下所示:

public int show(FragmentTransaction transaction, String tag) {
    return show(transaction, tag, false);
}


public int show(FragmentTransaction transaction, String tag, boolean allowStateLoss) {
    transaction.add(this, tag);
    mRemoved = false;
    mBackStackId = allowStateLoss ? transaction.commitAllowingStateLoss() : transaction.commit();
    return mBackStackId;
}

请注意,这是一个巨大的黑客。我实际上这样做的方式只是制作我自己的对话片段,我可以从原始片段中注册。当另一个对话片段做一些事情(比如被解雇)时,它告诉任何听众它正在消失。我是这样做的:

public static class PlayerPasswordFragment extends DialogFragment{

 Player toJoin;
 EditText passwordEdit;
 Button okButton;
 PlayerListFragment playerListFragment = null;

 public void onCreate(Bundle icicle){
   super.onCreate(icicle);
   toJoin = Player.unbundle(getArguments());
   Log.d(TAG, "Player id in PasswordFragment: " + toJoin.getId());
 }

 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle){
     View v = inflater.inflate(R.layout.player_password, container, false);
     passwordEdit = (EditText)v.findViewById(R.id.player_password_edit);
     okButton = (Button)v.findViewById(R.id.ok_button);
     okButton.setOnClickListener(new View.OnClickListener(){
       public void onClick(View v){
         passwordEntered();
       }
     });
     getDialog().setTitle(R.string.password_required);
     return v;
 }

 public void passwordEntered(){
   //TODO handle if they didn't type anything in
   playerListFragment.joinPlayer(toJoin, passwordEdit.getText().toString());
   dismiss();
 }

 public void registerPasswordEnteredListener(PlayerListFragment playerListFragment){
   this.playerListFragment = playerListFragment;
 }

 public void unregisterPasswordEnteredListener(){
   this.playerListFragment = null;
 }
}

所以现在我有办法在事情发生时通知 PlayerListFragment。请注意,适当地调用 unregisterPasswordEnteredListener 非常重要(在上述情况下,当 PlayerListFragment “消失”时),否则当该侦听器不再存在时,此对话框片段可能会尝试调用已注册侦听器上的函数。

于 2012-04-11T22:35:04.053 回答
26

@Natix留下的评论是一些人可能已经删除的快速单线。

解决这个问题的最简单方法是在运行您自己的代码之前调用super.onActivityResult() 。无论您是否使用支持库,这都有效,并在您的活动中保持行为一致性。

有:

我读得越多,我看到的疯狂黑客就越多。

如果您仍然遇到问题,那么Alex Lockwood的那个就是要检查的那个。

于 2015-05-24T23:49:32.947 回答
14

我相信这是一个Android错误。基本上,Android 在活动/片段生命周期中的错误点(在 onStart() 之前)调用 onActivityResult。

该错误报告在https://issuetracker.google.com/issues/36929762

我通过基本上将 Intent 存储为稍后在 onResume() 中处理的参数来解决它。

[编辑] 现在有更好的解决方案来解决这个问题,这些解决方案在 2012 年还没有。请参阅其他答案。

于 2012-04-11T21:54:35.703 回答
11

编辑:另一种选择,可能是最好的(或者,至少是支持库所期望的......)

如果您将 DialogFragments 与 Android 支持库一起使用,则应该使用 FragmentActivity 的子类。尝试以下操作:

onActivityResult(int requestCode, int resultCode, Intent data) {

   super.onActivityResult(requestCode, resultCode, intent);
   //other code

   ProgressFragment progFragment = new ProgressFragment();  
   progFragment.show(getActivity().getSupportFragmentManager(), PROG_DIALOG_TAG);

   // other code
}

我查看了 FragmentActivity 的源代码,看起来它正在调用内部片段管理器以便在不丢失状态的情况下恢复片段。


我找到了一个未在此处列出的解决方案。我创建一个 Handler,并在 Handler 中启动对话片段。所以,稍微编辑一下你的代码:

onActivityResult(int requestCode, int resultCode, Intent data) {

   //other code

   final FragmentManager manager = getActivity().getSupportFragmentManager();
   Handler handler = new Handler();
   handler.post(new Runnable() {
       public void run() {
           ProgressFragment progFragment = new ProgressFragment();  
           progFragment.show(manager, PROG_DIALOG_TAG);
       }
   }); 

  // other code
}

这对我来说似乎更干净,更不老套。

于 2013-07-29T23:08:14.007 回答
9

有两种 DialogFragment show() 方法 -show(FragmentManager manager, String tag)show(FragmentTransaction transaction, String tag).

如果您想使用该方法的 FragmentManager 版本(如在原始问题中),一个简单的解决方案是覆盖此方法并使用 commitAllowingStateLoss:

public class MyDialogFragment extends DialogFragment {

  @Override 
  public void show(FragmentManager manager, String tag) {
      FragmentTransaction ft = manager.beginTransaction();
      ft.add(this, tag);
      ft.commitAllowingStateLoss();
  }

}

覆盖show(FragmentTransaction, String)这种方式并不容易,因为它还应该修改原始 DialogFragment 代码中的一些内部变量,所以我不推荐它 - 如果您想使用该方法,请尝试接受的答案中的建议(或来自的评论)杰弗里·布拉特曼)。

使用 commitAllowingStateLoss 存在一些风险 - 文档声明“与 commit() 类似,但允许在保存活动状态后执行提交。这是危险的,因为如果活动需要稍后从其状态恢复,则提交可能会丢失, 所以这应该只用于 UI 状态可以在用户上意外更改的情况。”

于 2013-04-25T03:46:35.710 回答
4

在附加活动调用其方法 onSaveInstanceState() 后,您无法显示对话框。显然,onSaveInstanceState() 是在 onActivityResult() 之前调用的。所以你应该在这个回调方法 OnResumeFragment() 中显示你的对话框,你不需要重写 DialogFragment 的 show() 方法。希望这会帮助你。

于 2013-05-24T01:56:49.060 回答
3

我提出了第三种解决方案,部分基于 hmt 的解决方案。基本上,创建一个 DialogFragments 的 ArrayList,在 onResume() 上显示;

ArrayList<DialogFragment> dialogList=new ArrayList<DialogFragment>();

//Some function, like onActivityResults
{
    DialogFragment dialog=new DialogFragment();
    dialogList.add(dialog);
}


protected void onResume()
{
    super.onResume();
    while (!dialogList.isEmpty())
        dialogList.remove(0).show(getSupportFragmentManager(),"someDialog");
}
于 2012-12-18T03:21:19.833 回答
3

onActivityResult() 在 onResume() 之前执行。您需要在 onResume() 或更高版本中完成您的 UI。

使用布尔值或任何你需要的东西来传达结果已经在这两种方法之间返回。

... 就是这样。简单的。

于 2013-07-29T14:52:42.410 回答
2

我知道这是很久以前回答的......但是有一种比我在这里看到的其他一些答案更简单的方法......在我的具体情况下,我需要显示来自片段 onActivityResult() 的 DialogFragment方法。

这是我处理它的代码,它工作得很好:

DialogFragment myFrag; //Don't forget to instantiate this
FragmentTransaction trans = getActivity().getSupportFragmentManager().beginTransaction();
trans.add(myFrag, "MyDialogFragmentTag");
trans.commitAllowingStateLoss();

正如在其他一些帖子中提到的那样,如果您不小心,提交状态丢失可能会导致问题......在我的情况下,我只是向用户显示一条错误消息,并带有一个关闭对话框的按钮,所以如果失去的状态没什么大不了的。

希望这可以帮助...

于 2013-06-13T06:29:34.953 回答
2

这是一个老问题,但我认为我已经用最简单的方法解决了:

getActivity().runOnUiThread(new Runnable() {
    @Override
        public void run() {
            MsgUtils.toast(getString(R.string.msg_img_saved),
                    getActivity().getApplicationContext());
        }
    });
于 2015-10-01T13:41:07.110 回答
2

这是因为当调用#onActivityResult() 时,父活动已经调用了#onSaveInstanceState()

我会使用 Runnable 来“保存”#onActivityResult() 上的操作(显示对话框),以便稍后在活动准备好时使用它。

通过这种方法,我们确保我们想要的操作将始终有效

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == YOUR_REQUEST_CODE) {
        mRunnable = new Runnable() {
            @Override
            public void run() {
                showDialog();
            }
        };
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

@Override
public void onStart() {
    super.onStart();
    if (mRunnable != null) {
        mRunnable.run();
        mRunnable = null;
    }
}
于 2017-08-31T15:39:58.877 回答
0

我找到的最干净的解决方案是:

@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            onActivityResultDelayed(requestCode, resultCode, data);
        }
    });
}

public void onActivityResultDelayed(int requestCode, int resultCode, Intent data) {
    // Move your onActivityResult() code here.
}
于 2013-11-03T13:59:19.390 回答
0

我在活动时遇到此错误.show(getSupportFragmentManager(), "MyDialog");

先试试.show(getSupportFragmentManager().beginTransaction(), "MyDialog");

如果仍然无法正常工作,这篇文章(来自 onActivityResult 的 Show DialogFragment)可以帮助我解决问题。

于 2015-02-25T05:35:15.783 回答
0

其他方式:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case Activity.RESULT_OK:
            new Handler(new Handler.Callback() {
                @Override
                public boolean handleMessage(Message m) {
                    showErrorDialog(msg);
                    return false;
                }
            }).sendEmptyMessage(0);
            break;
        default:
            super.onActivityResult(requestCode, resultCode, data);
    }
}


private void showErrorDialog(String msg) {
    // build and show dialog here
}
于 2017-06-22T04:52:02.110 回答
-1

super.onActivityResult(requestCode, resultCode, data);在处理片段之前调用

于 2017-07-20T14:47:01.833 回答
-3

众所周知,这个问题是因为 onActivityResult() 在 onstart() 之前调用,所以只需在 onActivityResult() 开始时调用 onstart(),就像我在这段代码中所做的那样

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      onStart();
      //write you code here
}
于 2015-04-08T06:01:08.290 回答