显示 DialogFragment 会引发 "Can not perform this action after onSaveInstanceState" 错误
Showing a DialogFragment throws "Can not perform this action after onSaveInstanceState" error
问题
你好,我正在为 Titanium 创建一个 Android 和 iOS 模块,它有一个 sendLog 方法,可以向服务器发送一些任意 JSON 数据和 returns URL 如果它匹配一些预定义的过滤器。 URL 应该在带有 webview 的模态对话框中打开。
我编写了本机 iOS 和 Android 库并将它们包装为 Titanium 模块。在 iOS 上一切正常,但在 Android 上我无法打开对话框(请参阅下面的错误堆栈跟踪)。现在有一条日志消息总是触发同一个网页用于测试目的。在 Android 上它只是默默地失败了。
测试用例
var mupets = require("be.iminds.mupets");
mupets.initialize("wappr", "http://tocker.iminds.be:3000/log/report.json", 1, 100, 3);
var esmLog = {
bar: "foo"
};
mupets.sendLog("es-test-01",JSON.stringify(esmLog));
在这段代码之后(最多约 10 秒后),模块应显示一个本机对话框,其中包含以下网页:http://tocker.iminds.be:3000/es/sheets/test-01/index.html
相反,这是我一直得到的错误:
日志
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1411)
at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1429)
at android.app.BackStackRecord.commitInternal(BackStackRecord.java:687)
at android.app.BackStackRecord.commit(BackStackRecord.java:663)
at android.app.DialogFragment.show(DialogFragment.java:256)
at be.iminds.mupets_client_android.logging.plugins.OutHttp.getEsm(OutHttp.java:122)
at be.iminds.mupets_client_android.logging.plugins.OutHttp.success(OutHttp.java:78)
at be.iminds.mupets_client_android.HttpClient.onResponse(HttpClient.java:76)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:133)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
这是导致错误的 Android 代码:
Activity activity = (Activity) context;
EsmDialogFragment esmDialogFragment = EsmDialogFragment.newInstance(new EsmDialogListener() {
@Override
public void submit(String type, JsonObject result) {
Mupets.sendLog(type, result);
esmShown = false;
}
@Override
public void onCancel(JsonObject cancelled) {
super.onCancel(cancelled);
Mupets.sendLog("ESM_cancelled", cancelled);
esmShown = false;
}
}, url, true);
FragmentTransaction transaction = activity.getFragmentManager().beginTransaction();
Fragment prev = activity.getFragmentManager().findFragmentByTag(EsmDialogFragment.ESM_DIALOG_FRAGMENT);
if (prev != null) {
transaction.remove(prev);
}
transaction.addToBackStack(null);
Log.v(TAG, "Pre-show fragment");
esmDialogFragment.show(transaction, EsmDialogFragment.ESM_DIALOG_FRAGMENT);
Log.v(TAG, "Post-show fragment");
Titanium 是否不允许使用 Fragments/or 要求您在特定点调用 Dialog.show()?该错误涉及“...在 onSaveInstanceState 之后”,但我不明白如果我没有创建 activity 我将如何在 onSaveInstanceState 之前调用它以及为什么当我在本机 Android应用
这是一个 Titanium 示例项目,其中包含打开后应显示对话框的模块:
https://www.dropbox.com/s/0v77xd5gllv6kb3/testModule.zip?dl=1
这不是一个微不足道的问题,因此没有快速简单的解决方法可以让您仅 copy/paste 从答案中获得。最重要的是,您将不得不重构一些代码。
您正在尝试显示 DialogFragment
以响应异步操作 - 如果该操作在 onSaveInstanceState
之后完成,回调将尝试显示对话框并引发 IllegalStateException
.
保护自己免受此问题影响的方法是不要直接从回调中执行 UI 操作。相反,您需要推迟到开始或恢复 Activity
或 Fragment
,以便您可以安全地显示对话框。
一个简单的方法是使用粘性事件,即 post 来自回调的粘性事件并在 UI 组件的 onResume
中订阅该类型的粘性事件方法。
如果您不想使用事件总线库,您可以改为从非UI 组件调用异步方法,在回调中更新其内部状态,然后使 UI 组件检查 onResume
中的状态。如果您使用这种方法,则需要小心管理全局状态。
由于 dialogfragment 没有 commitAllowingStateLoss
的选项,我使用的最简单的解决方案是在调用 onSaveInstance 时设置一个标志并在 onCreate
和 onRestoreInstance
上重置它。然后在进行片段交易之前,检查标志以确保其为假。顺便说一句,这通常发生在异步回调中。当后台工作完成并触发回调时,activity 已经超出了 onSaveInstance。
我知道这个问题已经有了正确答案,但我想分享我的解决方案,要显示 DialogFragment,您应该覆盖它的 show()
方法并在 Transaction
对象上调用 commitAllowingStateLoss()
。这是 Kotlin 中的示例:
override fun show(manager: FragmentManager?, tag: String?) {
try {
val ft = manager?.beginTransaction()
ft?.add(this, tag)
ft?.commitAllowingStateLoss()
} catch (ignored: IllegalStateException) {
}
}
参考@Dennis 的评论,我重写了方法 show 并且有效。
@Override
public void show(FragmentManager manager, String tag) {
FragmentTransaction fragmentTransaction = manager.beginTransaction();
fragmentTransaction.add(this, TAG);
fragmentTransaction.commitAllowingStateLoss();
}
希望对您有所帮助
我遇到了同样的问题,并通过覆盖 DialogFragment 扩展中的 show() 解决了这个问题 class。
public class MyDialogFragment extends DialogFragment {
@Override
public void show(FragmentManager manager, String tag) {
try {
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commitAllowingStateLoss();
} catch (IllegalStateException e) {
Log.d("ABSDIALOGFRAG", "Exception", e);
}
}
}
问题
你好,我正在为 Titanium 创建一个 Android 和 iOS 模块,它有一个 sendLog 方法,可以向服务器发送一些任意 JSON 数据和 returns URL 如果它匹配一些预定义的过滤器。 URL 应该在带有 webview 的模态对话框中打开。
我编写了本机 iOS 和 Android 库并将它们包装为 Titanium 模块。在 iOS 上一切正常,但在 Android 上我无法打开对话框(请参阅下面的错误堆栈跟踪)。现在有一条日志消息总是触发同一个网页用于测试目的。在 Android 上它只是默默地失败了。
测试用例
var mupets = require("be.iminds.mupets");
mupets.initialize("wappr", "http://tocker.iminds.be:3000/log/report.json", 1, 100, 3);
var esmLog = {
bar: "foo"
};
mupets.sendLog("es-test-01",JSON.stringify(esmLog));
在这段代码之后(最多约 10 秒后),模块应显示一个本机对话框,其中包含以下网页:http://tocker.iminds.be:3000/es/sheets/test-01/index.html
相反,这是我一直得到的错误:
日志
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1411)
at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1429)
at android.app.BackStackRecord.commitInternal(BackStackRecord.java:687)
at android.app.BackStackRecord.commit(BackStackRecord.java:663)
at android.app.DialogFragment.show(DialogFragment.java:256)
at be.iminds.mupets_client_android.logging.plugins.OutHttp.getEsm(OutHttp.java:122)
at be.iminds.mupets_client_android.logging.plugins.OutHttp.success(OutHttp.java:78)
at be.iminds.mupets_client_android.HttpClient.onResponse(HttpClient.java:76)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:133)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
这是导致错误的 Android 代码:
Activity activity = (Activity) context;
EsmDialogFragment esmDialogFragment = EsmDialogFragment.newInstance(new EsmDialogListener() {
@Override
public void submit(String type, JsonObject result) {
Mupets.sendLog(type, result);
esmShown = false;
}
@Override
public void onCancel(JsonObject cancelled) {
super.onCancel(cancelled);
Mupets.sendLog("ESM_cancelled", cancelled);
esmShown = false;
}
}, url, true);
FragmentTransaction transaction = activity.getFragmentManager().beginTransaction();
Fragment prev = activity.getFragmentManager().findFragmentByTag(EsmDialogFragment.ESM_DIALOG_FRAGMENT);
if (prev != null) {
transaction.remove(prev);
}
transaction.addToBackStack(null);
Log.v(TAG, "Pre-show fragment");
esmDialogFragment.show(transaction, EsmDialogFragment.ESM_DIALOG_FRAGMENT);
Log.v(TAG, "Post-show fragment");
Titanium 是否不允许使用 Fragments/or 要求您在特定点调用 Dialog.show()?该错误涉及“...在 onSaveInstanceState 之后”,但我不明白如果我没有创建 activity 我将如何在 onSaveInstanceState 之前调用它以及为什么当我在本机 Android应用
这是一个 Titanium 示例项目,其中包含打开后应显示对话框的模块: https://www.dropbox.com/s/0v77xd5gllv6kb3/testModule.zip?dl=1
这不是一个微不足道的问题,因此没有快速简单的解决方法可以让您仅 copy/paste 从答案中获得。最重要的是,您将不得不重构一些代码。
您正在尝试显示 DialogFragment
以响应异步操作 - 如果该操作在 onSaveInstanceState
之后完成,回调将尝试显示对话框并引发 IllegalStateException
.
保护自己免受此问题影响的方法是不要直接从回调中执行 UI 操作。相反,您需要推迟到开始或恢复 Activity
或 Fragment
,以便您可以安全地显示对话框。
一个简单的方法是使用粘性事件,即 post 来自回调的粘性事件并在 UI 组件的 onResume
中订阅该类型的粘性事件方法。
如果您不想使用事件总线库,您可以改为从非UI 组件调用异步方法,在回调中更新其内部状态,然后使 UI 组件检查 onResume
中的状态。如果您使用这种方法,则需要小心管理全局状态。
由于 dialogfragment 没有 commitAllowingStateLoss
的选项,我使用的最简单的解决方案是在调用 onSaveInstance 时设置一个标志并在 onCreate
和 onRestoreInstance
上重置它。然后在进行片段交易之前,检查标志以确保其为假。顺便说一句,这通常发生在异步回调中。当后台工作完成并触发回调时,activity 已经超出了 onSaveInstance。
我知道这个问题已经有了正确答案,但我想分享我的解决方案,要显示 DialogFragment,您应该覆盖它的 show()
方法并在 Transaction
对象上调用 commitAllowingStateLoss()
。这是 Kotlin 中的示例:
override fun show(manager: FragmentManager?, tag: String?) {
try {
val ft = manager?.beginTransaction()
ft?.add(this, tag)
ft?.commitAllowingStateLoss()
} catch (ignored: IllegalStateException) {
}
}
参考@Dennis 的评论,我重写了方法 show 并且有效。
@Override
public void show(FragmentManager manager, String tag) {
FragmentTransaction fragmentTransaction = manager.beginTransaction();
fragmentTransaction.add(this, TAG);
fragmentTransaction.commitAllowingStateLoss();
}
希望对您有所帮助
我遇到了同样的问题,并通过覆盖 DialogFragment 扩展中的 show() 解决了这个问题 class。
public class MyDialogFragment extends DialogFragment {
@Override
public void show(FragmentManager manager, String tag) {
try {
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commitAllowingStateLoss();
} catch (IllegalStateException e) {
Log.d("ABSDIALOGFRAG", "Exception", e);
}
}
}