Dialogs from custom classes crash in Android O - android

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.

Related

Could not find active fragment with index -1

Activity fragment manager problem When change orientation:
Caused by: java.lang.IllegalStateException: Could not find active fragment with index -1
at
android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:3026)
at
android.support.v4.app.Fragment.restoreChildFragmentState(Fragment.java:1446)
at
android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1380)
at
android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1740)
at
com.motors.mobile.core.v2.DaggerIncludeBaseActivity.onCreate(DaggerIncludeBaseActivity.java:26)
Follow my code :
#Override
protected void tabletPortraitInit(Bundle savedInstanceState) {
super.tabletPortraitInit(savedInstanceState);
openSubFragment();
}
#Override
protected void tableLandscapeInit(Bundle savedInstanceState) {
super.tableLandscapeInit(savedInstanceState);
openSubFragment();
}
protected void openSubFragment() {
Bundle bundle = getIntent().getBundleExtra(CAR_DETAIL_KEY);
fragment = new BuyDetailFragment();
if (getSupportFragmentManager().findFragmentByTag(BuyDetailFragment.TAG) != null)
fragment = (BuyDetailFragment) getSupportFragmentManager().findFragmentByTag(BuyDetailFragment.TAG);
fragment.setArguments(bundle);
menuClickListener = fragment;
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.flMain, fragment, BuyDetailFragment.TAG)
.commit();
// init toolbar items
View tbView = getLayoutInflater().inflate(R.layout.items_detail_menu_layout, toolbar.findViewById(R.id.container), true);
phone = tbView.findViewById(R.id.phone);
message = tbView.findViewById(R.id.message);
link = tbView.findViewById(R.id.notifications);
site = tbView.findViewById(R.id.site);
shortlistView = tbView.findViewById(R.id.wishListMenu);
phone.setOnClickListener((e) -> menuClickListener.clickPhone());
message.setOnClickListener((e) -> menuClickListener.clickMessage());
link.setOnClickListener((e) -> menuClickListener.clickNotifications());
site.setOnClickListener((e) -> menuClickListener.clickSite());
shortlistView.setOnClickListener((e) -> menuClickListener.clickShortlist());
Drawable drawable = ContextCompat.getDrawable(this, R.drawable.vector_heart);
drawable.setColorFilter(ContextCompat.getColor(this, R.color.colorAccent), PorterDuff.Mode.SRC_IN);
shortlistView.changeIcon(drawable);
}
There is no my baseActivity
Does your fragment have setRetainInstance(true)? If so, that may be causing you an issue here, especially if you are using a fragment apart of FragmentStatePagerAdapter.
This can happen with a combination of dismissAllowingStateLoss after onSaveInstanceState and retainInstanceState.
See this helpful example with steps to reproduce (that site does not allow commenting, but it helped me diagnose the issue)
Steps to reproduce:
Open page and show dialog fragment with retainInstance = true
Background app, onSaveInstanceState is called
dismiss dialog in an async task via dismissAllowingStateLoss
perform configuration change, for example by changing language or orientation
open app
crash "Unable to start activity... java.lang.IllegalStateException: Could not find active fragment with index -1"
Under the scenes what's going on is that FragmentManagerImpl.restoreAllState now has an active fragment with an index of -1 because dismissAllowingStateLoss removes the fragment from the backstack, BUT, it is still part of nonConfigFragments because the commit part of dismissAllowingStateLoss was ignored as it was called after onSaveInstanceState.
To fix this will require one of:
not using retainInstanceState on Dialogs that can be dismissed via dismissAllowStateLoss, or
not calling dismiss after state loss
and implementing the desired behavior in a different way.

Application crashes on dialog dismiss

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.

java.lang.InstantiationException: can't instantiate class ... no empty constructor

I have found topics with similar questions like mine but cant find the answer I am looking for so far.
my application consists of a FragmentActivity which hosts a ViewPagerAdapter (child of FragmentPagerAdapter) with a fragment in each tab.
My ViewPagerAdapter is instantiated in the OnCreateView function of the parent activity
_adapter = new ViewPagerAdapter(getApplicationContext()
, getSupportFragmentManager()
, numOfTabs
, status);
The ViewPagerAdapter implements the minimum required methods getItem, getCount and getItemPosition
My getItem initializes a different Fragment for each position:
#Override
public Fragment getItem(int position)
{
Fragment f = new Fragment();
Log.d("Adbox",String.format("Inside ViewPagerAdapter.getItem(%s)",position));
switch(position)
{
case 0:
Log.d("Adbox","All offers ==");
f=FragmentAllOffers.newInstance(_context);
f.setRetainInstance(true);
break;
case 1:
Log.d("Adbox","Nearby offers ==");
f=FragmentNearbyOffers.newInstance(_context);
//f.setRetainInstance(true);
break;
case 2:
Log.d("Adbox","My coupons ==");
f=FragmentCoupons.newInstance(_context);
f.setRetainInstance(true);
break;
case 3:
Log.d("Adbox","Account ==");
f=FragmentAccount.newInstance(_context);
f.setRetainInstance(true);
//f=LayoutLocal.newInstance(_context);
break;
case 4:
Log.d("Adbox","Preferences ==");
f=FragmentPreferences.newInstance(_context);
f.setRetainInstance(true);
break;
default:
break;
}
return f;
}
The call to setRetainInstance(true) was added in my effort to resolve the problem I am facing but hasn't helped either.
Finally each of the Fragments above implement a public static newInstance() function with the application context as argument. For example the FragmentNearbyOffers contains the following:
public static android.support.v4.app.Fragment newInstance(Context ctx)
{
FragmentNearbyOffers f = new FragmentNearbyOffers();
ctx = context;
//Bundle bdl = new Bundle();
return f;
}
One more important information is that the parent activity is declared as singleInstance and I would like to keep this like this for some reasons.
Everything works fine but at some point when the activity is in the background for some time and I try to return to it either via the TaskManager or by clicking on the application icon I get the exception
android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.advisor.FragmentNearbyOffers$1: make sure class name exists, is public, and has an empty constructor that is public
The class name definitely exists, it's public and doesn't have a constructor which is like having an empty one..
I even added an empty constructor explicitly but this didn't help either, although I verified it is called.
From what I understood from various posts here is that Android when resuming the application, is placing in the FragmentPagerAdapter new instances of the fragments that are not linked to the original activity.. I verified this also because when calling the getActivity from inside the fragment I receive null..
But I don't understand why I am getting this Exception since there is an empty constructor...
I don't even know where to fix this, since the execution enters the onCreate of the activity, then immediately goes into the empty constructors of the fragments and then I get the exception.. Any other methods of the fragments i.e. onAttach, onCreate etc are not called at all..So it seems like it's actually crashing when constructing the fragments..
I am attaching the whole stacktrace I am getting just in case it helps:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.advisor/com.advisor.AdBoxWidgetConfigurationFragment}: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.advisor.FragmentNearbyOffers$1: make sure class name exists, is public, and has an empty constructor that is public
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2110)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2135)
at android.app.ActivityThread.access$700(ActivityThread.java:140)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1237)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4921)
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:1027)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.advisor.FragmentNearbyOffers$1: make sure class name exists, is public, and has an empty constructor that is public
at android.support.v4.app.Fragment.instantiate(Fragment.java:399)
at android.support.v4.app.FragmentState.instantiate(Fragment.java:97)
at android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1760)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:200)
at com.advisor.AdBoxWidgetConfigurationFragment.onCreate(AdBoxWidgetConfigurationFragment.java:60)
at android.app.Activity.performCreate(Activity.java:5206)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1094)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2074)
... 11 more
Caused by: java.lang.InstantiationException: can't instantiate class com.advisor.FragmentNearbyOffers$1; no empty constructor
at java.lang.Class.newInstanceImpl(Native Method)
at java.lang.Class.newInstance(Class.java:1319)
at android.support.v4.app.Fragment.instantiate(Fragment.java:388)
Notice the $1 at the end of the error. This is a reference to an anonymous class, not the fragment named FragmentNearbyOffers:
Unable to instantiate fragment com.advisor.FragmentNearbyOffers$1
Since fragments require a default constructor, and anonymous classes can never provide one, fragments must always be a named class. The Java Language Specification, section 15.9.5.1 states:
An anonymous class cannot have an explicitly declared constructor.
This section also explains that constructors are generated automatically, according to the context in which the anonymous class is declared. All of these constructors have parameters, thus they have a different signature than the default constructor. The combined effect is that anonymous classes can never have a constructor that matches the default constructor's signature.
You can either declare the fragment class in it's own file or declare it as a static nested class:
public static class NestedFragment extends BaseFragment { ...
Both of those methods should work just fine.
I managed to resolve the problem.. It was quite a tricky one because the exception message was misleading. Inside my fragment I had another nested fragment which I created in runtime with an inner class.
mMapFragment = new SupportMapFragment()
{
#Override
public void onResume()
{
// TODO Auto-generated method stub
super.onResume();
//initMap();
}
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
map = mMapFragment.getMap();
if (map != null)
{
// map.setMapType(GoogleMap.MAP_TYPE_NONE);
map.setMyLocationEnabled(true);
CameraUpdate center = CameraUpdateFactory.newLatLng(new LatLng(lat, lng));
zoom = CameraUpdateFactory.zoomTo(18);
map.moveCamera(center);
map.animateCamera(zoom);
}
}
};
Since after the application is killed by Android, when resuming back to it, android recreates the fragments and I am guessing that it couldnt recreate the nested one because the constructor of the inner class was not visible. I moved the class to a separate file as suggested in some other posts and it worked.

DialogFragment : NullPointerException (support library)

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();

Android RuntimeException onCreateDialog did not create a dialog for id

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

Categories

Resources