I'm using the excellent ACRA library to receive error reports from my apps.
I'm receiving a lot of reports from customers concerning an NPE in DialogFragment, but Im unable to reproduce it :
java.lang.NullPointerException
at android.support.v4.app.DialogFragment.onActivityCreated(SourceFile:366)
at android.support.v4.app.FragmentManagerImpl.moveToState(SourceFile:892)
at android.support.v4.app.FragmentManagerImpl.moveToState(SourceFile:1083)
at android.support.v4.app.FragmentManagerImpl.moveToState(SourceFile:1065)
at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(SourceFile:1844)
at android.support.v4.app.FragmentActivity.onStart(SourceFile:519)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1133)
at android.app.Activity.performStart(Activity.java:4475)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1929)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981)
at android.app.ActivityThread.access$600(ActivityThread.java:123)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4424)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
The NPE happens inside the support library (#line 366):
353 #Override
354 public void onActivityCreated(Bundle savedInstanceState) {
(...)
361 View view = getView();
362 if (view != null) {
363 if (view.getParent() != null) {
364 throw new IllegalStateException("DialogFragment can not be attached to a container view");
365 }
366 mDialog.setContentView(view);
367 }
Im unable to reproduce the problem on any of my device (from 2.2 to 4.1.1). Since there's no reference to any of my code, is it a library bug?
I have had to debug the same issue in a project.
Typically Dialog fragment is used as below
#Override
public Dialog onCreateDialog (Bundle savedInstanceState)
{
//Create custom dialog
return dialog;
}
Try updating it to the following
#Override
public Dialog onCreateDialog (Bundle savedInstanceState)
{
//Create custom dialog
if (dialog == null)
super.setShowsDialog (false);
return dialog;
}
This will prevent DialogFragment.onAtivityCreated() from executing methods on the null member variable mDialog.
This is a relatively common crash that I've seen reported within StackOverflow, and it's due to the dialog not being created properly, causing mDialog to be null. The brute force method I initially used to prevent the crash:
#Override
public void onActivityCreated(Bundle arg0) {
if (getDialog() == null ) { // Returns mDialog
// Tells DialogFragment to not use the fragment as a dialog, and so won't try to use mDialog
setShowsDialog( false );
}
super.onActivityCreated(arg0); // Will now complete and not crash
}
While the above is probably better than a crash, this doesn't address the root cause of why the dialog failed to be created. There could be many reasons for that and that's what needs to be debugged.
In my situation, I found that I needed to implement DialogFragment's onCreateDialog() instead of onCreateView() to properly create the dialog 100% of the time. (onCreateView() almost always works to create the dialog, but I PROVED that there are reproducible corner cases where onCreateView() fails to work, causing mDialog to become null. On the other hand, I always found that onCreateDialog() properly created DialogFragment's dialog.)
DialogFragment.mDialog can be null if DialogFragment.dismiss() is called before onActivityCreated() is called.
I had the NPE.
But surrounding the super.OnActivityCreated with a try/catch did not help.
What did help was the removal of a static field that was left over from copying an example.
So, no static fields inside an overridden DialogFragment.
No it's not. This is the common error if the SetContentView crashes. setContentView calls the constructors of the Controls of your view. One throwed a nullPointerException.
If you are overriding onCreateView(..) to instantiate the view of your DialogFragment you need to show it using a fragment transaction and put setShowsDialog to false to avoid this error. i.e:
//Instantiate your DialogFragment and fragmentManager previously and then just do this:
dialogFragment.setShowsDialog(false);
FragmentTransaction fT = fragmentManager.beginTransaction();
fT.add(0, dialogFragment, TAG);
fT.commit();
Related
I often need user interaction (dialogs) from custom classes that aren't subclasses of Activity or Fragment. Here's an example of how I'm currently doing this. I open the dialog from the fragment manager of the fragment that created the custom class, and I use a small inner class as the target so I can get the dialog result. This keeps all the related code in one place, as opposed to putting onActivityResult in the parent fragment:
public class DocumentViewer extends RelativeLayout {
public void deleteAnnotations() {
DialogFragment dialog = new DialogFragment();
Bundle args = new Bundle();
args.putString("title", this.app.getString(R.string.DELETE_ANNOTATIONS));
args.putString("message", this.app.getString(R.string.CONFIRM_DELETE_ANNOTATIONS));
args.putString("button1Text", this.app.getString(R.string.BUTTON_DELETE));
args.putString("button2Text", this.app.getString(R.string.BUTTON_CANCEL));
dialog.setArguments(args);
DocumentViewerAlertListener listener = new DocumentViewerAlertListener();
listener.canvas = this.canvas;
dialog.setTargetFragment(listener, R.id.confirmDelete);
dialog.show(this.fragment.getFragmentManager(), "confirmDelete");
}
public static class DocumentViewerAlertListener extends ALFragment {
public ALCanvas canvas;
#Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if ((requestCode == R.id.confirmDelete)&&(resultCode == 1)) {
this.canvas.clearItems();
}
}
}
}
Unfortunately, in Android O, the dialog.show line crashes with this stack trace:
09-20 14:06:12.852 24301-24301/com.bizname.appname E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.bizname.appname, PID: 24301
java.lang.IllegalStateException: Fragment ALAlert{1ace4cd #8 confirmDelete} declared target fragment DocumentViewerAlertListener{7e21182} that does not belong to this FragmentManager!
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1209)
at android.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1549)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1611)
at android.app.BackStackRecord.executeOps(BackStackRecord.java:807)
at android.app.FragmentManagerImpl.executeOps(FragmentManager.java:2394)
at android.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2189)
at android.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2142)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2049)
at android.app.FragmentManagerImpl$1.run(FragmentManager.java:718)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
It makes sense that the fragment manager I'm using doesn't know about this inner class, but what's the alternative? I tried using the inner class as the fragment manager...
dialog.show(listener.getFragmentManager(), "confirmDelete");
...and there was no crash, but the dialog didn't appear, perhaps because the listener fragment is never displayed.
I'm open to either a quick fix for my current approach, or a different approach that accomplishes the same thing (but would prefer a quick fix!).
While writing out the question, I had an idea based on "the listener fragment is never displayed." I added this line before showing the dialog:
this.fragment.getFragmentManager().beginTransaction().add(listener, "confirmDelete").commit();
I assume this lets the fragment manager know about my listener fragment, so it doesn't throw the exception.
This seems like an easy and logical fix, but I'm still open to other approaches.
I know there are number of solutions given for this question, but none of them worked for me. Even after having all of the checks, my application crashes on dismiss dialog.
Here is the code I am using :
public void dismissDialog(int dialogId) {
Dialog dialog = getDialog(dialogId);
Activity activity = mActivity.get();
if (activity != null && !activity.isFinishing()) {
if (dialog != null) {
if (dialog.isShowing()) {
dialog.dismiss();
}
mDialogsMap.remove(dialogId);
}
}
}
I am dismissing all the dialog using this method but still users are getting these crashes.
See logs :
Fatal Exception: java.lang.IllegalArgumentException: View=com.android.internal.policy.PhoneWindow$DecorView{144f7eb V.E...... R......D 0,0-684,240} not attached to window manager
at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:424)
at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:350)
at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:118)
at android.app.Dialog.dismissDialog(Dialog.java:362)
at android.app.Dialog.dismiss(Dialog.java:345)
at com.syntonic.freeway.android.DialogController.dismissDialog(SourceFile:64)
at com.syntonic.freeway.android.ui.OffersAndRewardDetailActivity$4.onSuccess(SourceFile:770)
at com.gs.jutil.web.NetworkCallback.onProgress(SourceFile:64)
at com.gs.jutil.web.NetworkCallback.validate(SourceFile:7)
at com.gs.jutil.web.NetworkCallback$1.run(SourceFile:46)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5451)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
It is because that the dialog is closed after the view has been destroyed... Try this
if (view.isShown()) {
dialog.dismiss()
}
Your issue is that you're trying to close a dialog that isn't attached.
java.lang.IllegalArgumentException: View=com.android.internal.policy.PhoneWindow$DecorView{144f7eb V.E...... R......D 0,0-684,240} not attached to window manager
Instead of passing around dialogId's, you should pass the dialog objects around. Are you storing a hashmap or list of dialogs that are open and trying to close them iteratively? What exactly are you trying to achieve? If you're passing the dialogs around, you can at least guarantee that they're attached, or at least check that they're still attached.
Here's an example of how I've managed clicks inside fragments
https://github.com/jmitchell38488/android-todo-app/blob/master/app/src/main/java/com/github/jmitchell38488/todo/app/ui/activity/ListActivity.java
List<DialogFragment> dialogs = new ArrayList<>();
dialogs.add(myNewDialog);
#Override
public void onDialogPositiveClick(DialogFragment dialog) {
if (some_code_that_does_not_dismiss) {
dialog.remove(dialog);
...
} else {
dialog.dismiss();
}
}
edit based on feedback:
If you're storing weak references (which I think is wrong - just how many dialogs can you have open at any time or stored in memory?), then you need to use a DialogManager to manage them, as well as a DialogFactory to instantiate them, on top of which, you need an abstract class that extends DialogFragment so that you can centralise your dismiss and management logic. That way all you need to do is:
DialogFactory.getInstance(/** args **/)
Then inside onStop or onDestroy:
// If you want to store them in the bundle
SerializedDialogManager = DialogManager.serialize();
// Else, delete them
DialogManager.shutdown();
Inside your shutdown, you check each dialog to see if it still exists and can be dismissed:
List<Integer> dialogIds = new ArrayList<>();
for (int id ; dialogIds) {
Dialog d = getDialog(id);
if (d != null && d.isShown()) {
d.dismiss();
}
}
But really, you should be handling your dismiss actions inside the dialogs.
Food for thought, but I personally think that you shouldn't bother too much with weak references and let the activities/dialogs deal with instantiation and shut down.
Previously, we have no problem displaying the following DialogFragment
// Triggered by button click.
private void openFromCloud() {
LoadFromCloudTaskFragment loadFromCloudTaskFragment = new LoadFromCloudTaskFragment();
FragmentManager fm = this.getSupportFragmentManager();
fm.beginTransaction().add(loadFromCloudTaskFragment, "loadFromCloudTaskFragment").commit();
}
However, if we tend to display the same DialogFragment after pressing OK button on the following Intent, an error will occur.
private void openFromCloud() {
startActivityForResult(Utils.getGoogleAccountCredential().newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}
#Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
switch (requestCode) {
case REQUEST_ACCOUNT_PICKER:
if (resultCode == RESULT_OK && data != null && data.getExtras() != null) {
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
Utils.getGoogleAccountCredential().setSelectedAccountName(accountName);
LoadFromCloudTaskFragment loadFromCloudTaskFragment = new LoadFromCloudTaskFragment();
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().add(loadFromCloudTaskFragment, "loadFromCloudTaskFragment").commit();
}
}
break;
}
}
Here are the detailed error log
FATAL EXCEPTION: main
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { (has extras) }} to activity {org.yccheok.xxx.gui/org.yccheok.xxx.gui.XXXFragmentActivity}: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.ActivityThread.deliverResults(ActivityThread.java:3141)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3184)
at android.app.ActivityThread.access$1100(ActivityThread.java:130)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1243)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1299)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1310)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:541)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:525)
at org.yccheok.xxx.gui.XXXFragmentActivity$1.run(XXXFragmentActivity.java:107)
at android.app.Activity.runOnUiThread(Activity.java:4591)
at org.yccheok.xxx.gui.XXXFragmentActivity.onActivityResult(XXXFragmentActivity.java:102)
at android.app.Activity.dispatchActivityResult(Activity.java:5192)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3137)
... 11 more
I can simply "solve" the problem by using commitAllowingStateLoss instead of commit.
fm.beginTransaction().add(loadFromCloudTaskFragment, "loadFromCloudTaskFragment").commitAllowingStateLoss();
I don't really understand the documentation regarding commitAllowingStateLoss.
Like commit() but allows the commit to be executed after an activity's
state is saved. This is dangerous because the commit can be lost if
the activity needs to later be restored from its state, so this should
only be used for cases where it is okay for the UI state to change
unexpectedly on the user.
This is based on suggestion from getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState"
I don't really get the point it is okay for the UI state to change unexpectedly on the user.? May I know what is the possible side effect for using commitAllowingStateLoss? Any steps I can produce such side effect?
The only thing I can think of, is kind of "race condition" event.
Imagine a situation, when device is being rotated just before your commitAllowingStateLoss() call. AFAIK, the following occurs:
onSaveInstanceState() callback(activity store it's state with no fragment present at the moment(since you haven't committed anything yet)
commitAllowingStateLoss is executed adding fragment to the activity
Activity is recreated, restoring it's state to the moment, when there were no your fragment added
In my opinion, it cause hardly predictable situations, such as:
java.lang.IllegalStateException: Failure saving state: FragmentB has target not in fragment manager: FragmentA if you are using Fragment.setTargetFragment() for any reason
your fragment can simply be missing from the view
Anyway, I'm not 100% sure about that, but I'm having lots of unexpected java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState exceptions in my app, and trying to find a solution too.
Probably you don't have this problem anymore. I got a similar problem. I solved put fragment transition code in onResume function. I have put a flag to indicate if result was ok. If it's true then inside onResume method I did transition to other Fragment.
You can see the follow code in Activity.java
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
key codes ===>mFragments.saveAllState()
if you commit after onSaveInstanceState then the state of the fragment will not be saved
My activity invokes the camera with the ACTION_IMAGE_CAPTURE intent. If the camera activity returns succesfully, I set a flag in the onActivityResult callback, and based on the value of the flag I start a fragment in my onResume to add a caption to the image that was captured. This seems to work ok.
I just got a stack trace from the "wild" complaining that I was trying to commit a fragment transaction after onSaveInstanceState has been called. But I'm doing the commit in my onResume method! Why would android complain about this? I do have android:configChanges="orientation|keyboardHidden|keyboard|screenSize" set in my AndroidManifest.xml, so an orientation change should not trigger this....
This occurred on a Samsung Galaxy S3 (SGH-i747) running 4.0.4
Here is the stack:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1314)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1325)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:548)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:532)
at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.performFragmentTransition(AddPhotosActivity2.java:278)
at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.switchToCaptionsFragment(AddPhotosActivity2.java:438)
at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.onResume(AddPhotosActivity2.java:167)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1158)
at android.app.Activity.performResume(Activity.java:4544)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2448)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2486)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1187)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4514)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
at dalvik.system.NativeStart.main(Native Method)
Any help or wisdom is appreciated.
I think I know the answer - I'm using the FragmentActivity from v4 compatibility library, and so I need to perform my fragment transactions in onResumeFragments instead of in onResume. Can someone confirm?
you can use the method commitAllowingStateLoss()
but be aware you can lose the state of your activity
as you can see in google's android reference
which explain the different between the two in the following way
Like commit() but allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user.
from my experience it may cause the addToBackStack method not to work sometimes so you will need to add it manually on the fragment
and of course the state won't be saved (textbox text ext.)
this worked for me... found this out on my own... hope it helps you!
1) do NOT have a global "static" FragmentManager / FragmentTransaction.
2) onCreate, ALWAYS initialize the FragmentManager again!
sample below :-
public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSavedInstanceState = savedInstanceState;
setDefaultFragments();
}
protected void setDefaultFragments() {
fragmentManager = getSupportFragmentManager();
//check if on orientation change.. do not re-add fragments!
if(mSavedInstanceState == null) {
//instantiate the fragment manager
fragmentTransaction = fragmentManager.beginTransaction();
//the navigation fragments
NavigationFragment navFrag = new NavigationFragment();
ToolbarFragment toolFrag = new ToolbarFragment();
fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
fragmentTransaction.commitAllowingStateLoss();
//add own fragment to the nav (abstract method)
setOwnFragment();
}
}
Update I think I have found an explanation and a solution here: http://code.google.com/p/android/issues/detail?id=23096#c4
I implemented the Empty Fragment Workaround posted there and got no more IllegalStateException so far.
I add the invisible state fragment in my activity like this;
#Override
protected void onCreate(final Bundle args) {
...
if (args == null) {
final FragmentManager fm = this.getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
final Fragment emptyFragmentWithCallback = new EmptyFragmentWithCallbackOnResume();
ft.add(emptyFragmentWithCallback, EmptyFragmentWithCallbackOnResume.TAG);
ft.commit();
}
The following code is taken from above link:
public class EmptyFragmentWithCallbackOnResume extends Fragment {
OnFragmentAttachedListener mListener = null;
#Override
public void onAttach(SupportActivity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentAttachedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnFragmentAttachedListener");
}
}
#Override
public void onResume() {
super.onResume();
if (mListener != null) {
mListener.OnFragmentAttached();
}
}
public interface OnFragmentAttachedListener {
public void OnFragmentAttached();
}
}
and invoke fragment transactions that would intuitively go into onResume or onResumeFragments into my custom onFragmentAttached method that is invoked by the invisible state fragment. I do not use onResumeFragments at all and do not issue any fragment transactions in onResume.
So, to sum it up. If you are using the support lib and fragments, pretty much forget about onResume, forget about onResumeFragments and implement your own "onResume" based on the above workaround.
This is somewhat ridiculous.
I cannot confirm. I have the exact same problem. even though I am issuing fragment transaction in onResumeFragments.
This used to work as I posted here: IllegalStateException - Fragment support library .
It seems the error only occurs on 4.0.3 and 4.0.4. However it does neither occur always nor in my Emulator.
I am using support lib rev. 10 and API 16.
I call DialogFragment.show in onResumeFragments and continously get this ridiculous exception from some random users. I cannot reproduce it locally.
I was always getting this when I tried to show fragment in onActivityForResult() method, so the problem was next:
My Activity is paused and stopped, which means, that onSaveInstanceState() was called already (for both pre-Honeycomb and post-Honeycomb devices).
In case of any result I made transaction to show/hide fragment, which causes this IllegalStateException.
What I made is next:
Added value for determining if action I want was done (e.g. taking photo from camere - isPhotoTaken) - it can be boolean or integer value depending how much different transactions you need.
In overriden onResumeFragments() method I checked for my value and after made fragment transactions I needed. In this case commit() was not done after onSaveInstanceState, as state was returned in onResumeFragments() method.
I first develop my app targeting android 2.2 (SDK 8), using the support v4 library, and when I start using it with 4.2 (SDK 17), I had the same troubles with my fragments. But changing my Manifest to android:minSdkVersion="8" android:targetSdkVersion="17", solved my problems. Maybe this hepls you too.
Add this to your Activity:
#Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
I've an application that you can show and close several Dialogs with:
showDialog(...)
removeDialog(...)
I play a little bit with the application and when there is no any Dialog on the screen, I press the menu button and I go to the main android screen.
After a while, I enter again into my application and sometimes, I get this RuntimeException:
java.lang.IllegalArgumentException: Activity#onCreateDialog did not create a dialog for id 4
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2596)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2621)
at android.app.ActivityThread.access$2200(ActivityThread.java:126)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1932)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4595)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException: Activity#onCreateDialog did not create a dialog for id 4
at android.app.Activity.createDialog(Activity.java:878)
at android.app.Activity.restoreManagedDialogs(Activity.java:867)
at android.app.Activity.performRestoreInstanceState(Activity.java:815)
at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1096)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2565)
... 11 more
Any idea?
Thank you very much.
UPDATE, more information:
The current onCreateDialog implementation is:
protected Dialog onCreateDialog(int id){
Builder b = new AlertDialog.Builder(this);
if(id == 4){
b.setMessage(...);
b.setItems(items, new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which){
Intent i = new Intent(Current.this, Another.class);
startActivity(i);
}
});
return b.create();
}
return null;
}
In order to call this function I do:
removeDialog(4);
showDialog(4);
In API level 8, onCreateDialog(int) was deprecated in favor of onCreateDialog(int,Bundle). If you implement only the latter method and run the app on a device with an API level lower than 8, you get the described error message.
The solution is to implement onCreateDialog(int)
For SDK version < 8, if you return null in onCreateDialog you get Exception java.lang.IllegalArgumentException.
After experiencing this same issue (and finding that calling removeDialog from within onPause doesn't work reliably), I developed a workaround that seems to function (although it's admittedly a hack).
As seen in the grepcode link posted by antslava, in method performRestoreInstanceState, onRestoreInstanceState is called right before restoreManagedDialogs and is passed the same instance of Bundle savedInstanceState.
final void performRestoreInstanceState(Bundle savedInstanceState) {
onRestoreInstanceState(savedInstanceState);
restoreManagedDialogs(savedInstanceState);
}
Thus, there is opportunity to modify the Bundle savedInstanceState that is passed to restoreManagedDialogs from within the onRestoreInstanceState method.
To prevent any and all managed dialogs from being restored, one could implement onRestoreInstanceState in the following way:
// This same variable is defined as private in the Activity class. I need
// access to it, so I redefine it here.
private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
final Bundle b = savedInstanceState.getBundle(SAVED_DIALOGS_TAG);
if (null != b) {
savedInstanceState.remove(SAVED_DIALOGS_TAG);
}
}
This causes the Bundle referenced by key "android:savedDialogs" to be removed from Bundle savedInstanceState, which subsequently causes the call to restoreManagedDialogs to immediately return when it finds that this key cannot be found:
private void restoreManagedDialogs(Bundle savedInstanceState) {
final Bundle b = savedInstanceState.getBundle(SAVED_DIALOGS_TAG);
if (b == null) {
return;
}
...
}
This will cause onCreateDialog to not be called while restoring the Activity, effectively "hiding" any dialogs, thus preventing the scenario where one must return null from onCreateDialog from occurring.
This isn't a 'one size fits all' solution, but given my requirements it seems to fit the bill. By reviewing the code in grepcode for several platform versions (1.6, 2.1, 2.2, 2.2.2, and 4.0.3), it appears that this solution should work consistently given these existing implementations.
Have you implemented OnCreateDialog as presented here? When you call showDialog(4) for the first time, OnCreateDialog(4) will be called and you need to create the dialog and return it from this method.
Are you properly returning the dialog in onCreateDialog? If you were to do dialog.show() in dialog create but return some other dialog you could perhaps get a result like that.
Or are you doing any sort of manipulation in of the dialog object in onPrepareDialog