显示 onActivityResult 中的 Dialog片段

在 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 支持包。

39360 次浏览

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

https://issuetracker.google.com/issues/36929762报告了这个错误

我解决这个问题的方法基本上是将意图作为一个参数存储,然后在 onResume ()中进行处理。

现在有更好的解决方案,这在2012年是不可能的。

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

——原文——

实际上它是一个带有支持包的 已知窃听器(编辑: 实际上不是一个 bug。见@alex-lockwood 的评论)。在 bug 报告的评论中发布的一个工作是修改 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“消失”时) ,否则这个对话框片段可能会尝试在已注册的侦听器不再存在时调用该侦听器上的函数。

我提出了第三个解决方案,部分基于 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");
}

如果您使用 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);
}
}
}

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

如果您想使用该方法的 FragmentManager 版本(就像在最初的问题中那样) ,一个简单的解决方案是覆盖该方法并使用 committee AllowingStateloss:

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 代码中的一些内部变量,所以我不推荐使用这种方法——如果你想使用这种方法,那么试试接受答案中的建议(或者 Jeffrey Blattman 的评论)。

使用 commAllowingStateloss 存在一些风险——文档说明“ Like commit () ,但允许在保存活动状态之后执行提交。这很危险,因为如果以后需要从其状态恢复活动,则提交可能会丢失,所以只有在 UI 状态可以在用户身上意外改变的情况下才应该使用这种方法。”

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

I know this was answered quite a while ago.. but there is a much easier way to do this than some of the other answers I saw on here... In my specific case I needed to show a DialogFragment from a fragments onActivityResult() method.

下面是我处理这个问题的代码,它运行得很好:

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

正如在其他一些文章中提到的,如果你不小心,带有状态丢失的提交可能会导致问题... ... 在我的例子中,我只是用一个按钮向用户显示一个错误消息来关闭对话框,所以如果状态丢失了,这并不是什么大问题。

希望这个能帮上忙。

OnActivityResult ()在 onResume ()之前执行。

使用一个布尔值或者任何你需要的东西来传达这两个方法之间的结果。

就这么简单。

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

如果你在 Android 支持库中使用的是 DialogFragments,那么你应该使用的是 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
}

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


我找到了一个没有列在这里的解决方案。我创建一个 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
}

我觉得这样干净利落。

我找到的最干净的解决办法是:

@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.
}

我在活动中执行 .show(getSupportFragmentManager(), "MyDialog");时得到了这个错误。

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

如果仍然不工作,这篇文章(显示 onActivityResult 中的 Dialog片段)帮助我解决这个问题。

大家都知道这个问题是因为 onActivityResult ()在 onstart ()之前调用了 onstart () ,所以只需要在 onActivityResult ()的 start 中调用 onstart () ,就像我在这段代码中所做的那样

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
onStart();
//write you code here
}

@ Natix留下的评论是一个快速的一行程序,一些人可能已经删除了。

这个问题最简单的解决方案是在运行自己的代码之前调用 OnActivityResult ()。无论您是否使用支持库,这都可以工作,并且在您的活动中保持行为的一致性。

有:

我越了解这件事,就越觉得这是个疯狂的黑客行为。

如果您仍然遇到问题,那么 亚历克斯 · 洛克伍德的问题就是需要检查的问题。

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

getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
MsgUtils.toast(getString(R.string.msg_img_saved),
getActivity().getApplicationContext());
}
});

另一种方式:

@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
}

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

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

我将使用 Runnable“保存”# onActivityResult ()上的操作(show 对话框) ,以便稍后在活动准备就绪时使用。

通过这种方法,我们可以确保我们想要采取的行动总是有效的

@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;
}
}