IllegalStateException while showing DialogFragment after back from AppCompatActivity - android

I am starting a FragmentActivity from a custom DialogFragment and after I come back the original Activity and try to open the dialog again I get:
IllegalStateException: Can not perform this action after onSaveInstanceState
I don't understand why is it happening, if I dismiss the dialog from the original activity than I can show it again as many times as I want, but if I start a new Activity from the DialogFragment after that I cannot show any dialog again because I get the exception.
Here is my code, this method is in my DialogFragment:
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3){
mSearchLocationType = position;
switch(position){
case PSL_CURRENT_POSITION: // I can show the dialog again after this.
break;
case PSL_MAP_POINT:
Intent selectMapPoint = new Intent(parentActivity, SelectMapPointActivity.class);
selectMapPoint.putExtra(SelectMapPointFragment.EXTRA_SELECTED_POS, mSearchLocation);
parentActivity.startActivityForResult(selectMapPoint, REQ_MAP_POINT);
// After returning this Activity and trying to show ANY DialogFragment the app crashes.
break;
case PSL_ADDRESS:
Intent selectAddress = new Intent(parentActivity, SelectAddressActivity.class);
parentActivity.startActivity(selectAddress); // Also from this Activity, makes no difference.
break;
}
dismiss();
}
I am showing the dialog like:
if(!selectPoiAroundDialog.isVisible())
selectPoiAroundDialog.show(parentActivity.getSupportFragmentManager(), "mSelectPoiCategoryDialog");
What I have tried already:
Override the show() method of the DialogFragment and use this code to show the dialog:
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commitAllowingStateLoss();
Debug the app and check if all the lifecycle methods are called, but it seems normal to me, each of the times the onDestroyView() method of the DialogFragment is called.
Override the show() and onDismiss() method of the DialogFragment and track if it is shown or not.
This is a corporate app and has a bad design, so the class which I am trying to modify (where I am showing the dialog) is not child of Activity or Fragment. ´parentActivity´ is accessed by a static method of the application class, but I am sure that it has the right Activity. Do you think this can cause the problem?
EDIT:
The full stacktrace is when I useing commitAllowStateLoss():
java.lang.IllegalStateException: Activity has been destroyed
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1515)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:638)
at android.support.v4.app.BackStackRecord.commitAllowingStateLoss(BackStackRecord.java:621)
at com.mycompany.dialog.GTDialog.show(GTDialog.java:174)
at com.mycompany.fragment.content.SearchOfflinePOIContent$2.onClick(SearchOfflinePOIContent.java:250)
at android.view.View.performClick(View.java:5201)
at android.view.View$PerformClick.run(View.java:21209)
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:5525)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
I also tried to override the onSaveInstanceState() method, it didn't helped.
EDIT2:
The original stacktrace is:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1493)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1511)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:638)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:617)
at android.support.v4.app.DialogFragment.show(DialogFragment.java:139)
at com.mycompany.dialog.GTDialog.show(GTDialog.java:174)
at com.mycompany.fragment.content.SearchOfflinePOIContent$1.onClick(SearchOfflinePOIContent.java:197)
at android.view.View.performClick(View.java:5201)
at android.view.View$PerformClick.run(View.java:21209)
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:5525)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
If I override the show() method and try to commit the fragment with commitAllowingStateLoss() than I get the first exception.

I figured out the problem. When the app quit from an Activity it didn't remove the reference of it from parentActivity, so when I tried to show the dialog again I gave a reference of the destroyed activity.
Thanks for your time!

Related

How to call FragmentManager.popBackStack when activity is paused?

I have following situation
Let's take an example:
I have FirstActivity which contains Two fragments. Fragment-A is by default loaded to FirstActivity.By clicking on a button in Fragment-A screen moves to Fragment-B and it will add to back stack.From Fragment-B there is a button from where i visit to second activity. Now if any event occurs i am calling FragmentManager.popBackStack method to move Fragment-B to Fragment-A(This is application requirement) at that time my application get crashed. Because i am on SecondActivity and FragmentManager.popBackStack call from FirstActivity.
Now my code
When moving from Fragment-A to Fragment-B
MenuFragment menuFragment = MenuFragment.newInstance(orderObject.getOrderTableName(), orderObject.getTableSize(), arraySeatCount[k], arrayTableCode[k], arrayTableId[k], floorDetailObject.getId(), TabRabbitConstant.OrderType.DINEIN.orderType, "", "", false);
menuFragment.setTargetFragment(FloorFragment.this, UserDefault.ORDER_MENU);
FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameContainer, menuFragment, getResources().getString(R.string.M_MENU));
fragmentTransaction.addToBackStack(getResources().getString(R.string.M_MENU));
fragmentTransaction.commit();
When any event occur i am calling popbackstack on FirstActivity
FragmentManager manager = getSupportFragmentManager();
manager.popBackStack();
Exception i get when my application getting crash is
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1842)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1860)
at android.support.v4.app.FragmentManagerImpl.popBackStack(FragmentManager.java:770)
at com.app.tabrabbitpos.MainActivity.parseResponseInMain(MainActivity.java:691)
at com.app.tabrabbitpos.base.BaseActivity$3.run(BaseActivity.java:192)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Documentation of FragmentManager.popBackStack wrote that
This function is asynchronous -- it enqueues the request to pop, but the action will not be performed until the application returns to its event loop.
So according to documentation i thought it should work but it gives me exception and my application get crashed.
I have already checked this link for my issue but not getting any solution
Is it safe to call FragmentManager.popBackStack when activity is paused
In short my question is
How to call FragmentManager.popBackStack when activity is paused? (Means when i am on second activity and FirstActivity is on Pause)
Create a boolean to check if activity is running for the first time like this
private Boolean mFirstRun=false;
Then in first activity onResume check if the activity is running for the first time and perform the action.
#Override
protected void onResume() {
super.onResume();
if(!mFirstRun){
mFirstRun=true;
}else {
FragmentManager manager = getSupportFragmentManager();
manager.popBackStack();
mFirstRun=false;
}
}
When the event is fired in second activity dont forget call finish() in second activity which will resume the first activity again.

Dialogs from custom classes crash in Android O

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.

android.app.Application cannot be cast to android.support.v7.app.AppCompatActivity

I'm trying to start a Fragment inside my RecycleAdapter but when cast my Context with the AppCompatActivity the App crashed and inside logcat I got the message that android.app.Application cannot be cast to android.support.v7.app.AppCompatActivity.
here is my onClickListener inside RecycleAdapter.
holder.ItemClickButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction fragmentTransaction;
Toast.makeText(CTX,"Row Clicked id : "+homeCycleDataProvider.getId(),Toast.LENGTH_SHORT).show();
ItemsFragment fragobj = new ItemsFragment();
FragmentManager manager = ((AppCompatActivity) CTX).getSupportFragmentManager();
fragmentTransaction = manager.beginTransaction()
fragmentTransaction.replace(R.id.main_container, fragobj);
fragmentTransaction.commit();
}
});
here is my Adapter Initialization where I pass the context.
adapter = new ItemsRecycleAdapter(arrayList,getContext());
LOGCAT.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.a1appstudio.sabirhossain.xpresdelivery, PID: 3109
java.lang.ClassCastException: android.app.Application cannot be cast to android.support.v7.app.AppCompatActivity
at com.a1appstudio.sabirhossain.xpresdelivery.ItemsListViewPackage.ItemsRecycleAdapter$1.onClick(ItemsRecycleAdapter.java:66)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
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:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
A context is not always an Activity, even when you're in an Activity. It could be an Application, or a wrapper around another context. It's almost always wrong to cast a Context to an Activity. If you absolutely need one, you should pass in an Activity as a parameter, rather than a Context. Or better yet, pass in the support fragment manager directly rather than the activity, since that's all you need it for.
adapter = new ItemsRecycleAdapter(arrayList,getActivity());
you can try this
if (context instanceof MainActivity ) {
MainActivity myActivity = (MainActivity)context;
myActivity.getSupportFragmentManager();
}

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.

AlertDialog causes leaks when activity finishes

I have a base Activity that extends FragmentActivity. In this activity I have a dialog with an OK button. The dialog says there's no internet connection. After a user clicks on the button the activity finishes.
I'm aware that before exiting an activity I must call dismiss on an AlertDialog object. I tried to do that with two ways: in the onClick method of the DialogInterface.OnClickListener object
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
BaseActivity.this.finish();
}
});
After I realized the previous code didn't work I tried to make a member variable in my BaseActivity class and calling the dismiss method onDestroy:
#Override
protected void onDestroy() {
super.onDestroy();
if (alertDialog != null) {
alertDialog.dismiss();
alertDialog = null;
}
}
It didn't work either. I still get this in my debugger:
01-07 01:43:10.815 3055-3055/com.example.app E/WindowManager﹕ android.view.WindowLeaked: Activity com.example.app.ArticlesActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{1a183701 V.E..... R....... 0,0-1026,640} that was originally added here
at android.view.ViewRootImpl.<init>(ViewRootImpl.java:363)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:261)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
at android.app.Dialog.show(Dialog.java:298)
at com.example.app.BaseActivity.showErrorDialog(BaseActivity.java:145)
at com.example.app.BaseActivity.onCreate(BaseActivity.java:57)
at com.example.app.ArticlesActivity.onCreate(ArticlesActivity.java:30)
at android.app.Activity.performCreate(Activity.java:5933)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2251)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
at android.app.ActivityThread.access$800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Thank you in advance for your help.
You would want to extend DialogFragment instead. There in OnCreateDialog() you will return your AlertDialog. You show the fragment with show() and dismiss with dismiss(). This guarantees no leaks.
I would declare an interface within the dialog class and let the activity implement that interface. Then set a public method within the activity, which your dialog can call and pass on teh result.
if it is determined, that you need to finish / exit the activity, rather than the dialog calling the finish directly, it would just call the public method and that method inside the activity will actually finish the activity.
Refer this link for how this is done -
How to get button clicks in host fragment from dialog fragment
http://developer.android.com/training/basics/fragments/communicating.html
I figured it out. Actually there were two dialogs. One of them I dismissed and the second one was still around, that's why I got the error. Thank you guys though, you gave me valuable information to think.

Categories

Resources